Compare commits
4 Commits
b688ded610
...
ba55cbcbd3
Author | SHA1 | Date |
---|---|---|
Lykin | ba55cbcbd3 | |
Lykin | 686f73c3dd | |
Lykin | 464a85867d | |
Lykin | af6b4257f9 |
|
@ -46,6 +46,7 @@ type connectionItem struct {
|
|||
cursor map[int]uint64 // current cursor of databases
|
||||
entryCursor map[int]entryCursor // current entry cursor of databases
|
||||
stepSize int64
|
||||
db int // current database index
|
||||
}
|
||||
|
||||
type browserService struct {
|
||||
|
@ -288,10 +289,14 @@ func (b *browserService) getRedisClient(connName string, db int) (item connectio
|
|||
b.connMap[connName] = item
|
||||
}
|
||||
|
||||
if db >= 0 {
|
||||
if db >= 0 && item.db != db {
|
||||
var rdb *redis.Client
|
||||
if rdb, ok = client.(*redis.Client); ok && rdb != nil {
|
||||
_ = rdb.Do(item.ctx, "select", strconv.Itoa(db)).Err()
|
||||
if err = rdb.Do(item.ctx, "select", strconv.Itoa(db)).Err(); err != nil {
|
||||
return
|
||||
}
|
||||
item.db = db
|
||||
b.connMap[connName] = item
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -299,21 +304,8 @@ func (b *browserService) getRedisClient(connName string, db int) (item connectio
|
|||
|
||||
// load current database size
|
||||
func (b *browserService) loadDBSize(ctx context.Context, client redis.UniversalClient) int64 {
|
||||
if cluster, isCluster := client.(*redis.ClusterClient); isCluster {
|
||||
var keyCount atomic.Int64
|
||||
cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
|
||||
if size, serr := cli.DBSize(ctx).Result(); serr != nil {
|
||||
return serr
|
||||
} else {
|
||||
keyCount.Add(size)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return keyCount.Load()
|
||||
} else {
|
||||
keyCount, _ := client.DBSize(ctx).Result()
|
||||
return keyCount
|
||||
}
|
||||
keyCount, _ := client.DBSize(ctx).Result()
|
||||
return keyCount
|
||||
}
|
||||
|
||||
// save current scan cursor
|
||||
|
@ -387,10 +379,10 @@ func (b *browserService) ServerInfo(name string) (resp types.JSResp) {
|
|||
|
||||
// OpenDatabase open select database, and list all keys
|
||||
// @param path contain connection name and db name
|
||||
func (b *browserService) OpenDatabase(connName string, db int, match string, keyType string) (resp types.JSResp) {
|
||||
b.setClientCursor(connName, db, 0)
|
||||
func (b *browserService) OpenDatabase(server string, db int) (resp types.JSResp) {
|
||||
b.setClientCursor(server, db, 0)
|
||||
|
||||
item, err := b.getRedisClient(connName, db)
|
||||
item, err := b.getRedisClient(server, db)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
|
@ -516,6 +508,35 @@ func (b *browserService) LoadAllKeys(connName string, db int, match, keyType str
|
|||
return
|
||||
}
|
||||
|
||||
func (b *browserService) GetKeyType(param types.KeySummaryParam) (resp types.JSResp) {
|
||||
item, err := b.getRedisClient(param.Server, param.DB)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
client, ctx := item.client, item.ctx
|
||||
key := strutil.DecodeRedisKey(param.Key)
|
||||
var keyType string
|
||||
keyType, err = client.Type(ctx, key).Result()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if keyType == "none" {
|
||||
resp.Msg = "key not exists"
|
||||
return
|
||||
}
|
||||
|
||||
var data types.KeySummary
|
||||
data.Type = strings.ToLower(keyType)
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = data
|
||||
return
|
||||
}
|
||||
|
||||
// GetKeySummary get key summary info
|
||||
func (b *browserService) GetKeySummary(param types.KeySummaryParam) (resp types.JSResp) {
|
||||
item, err := b.getRedisClient(param.Server, param.DB)
|
||||
|
|
|
@ -14,9 +14,9 @@ type KeySummaryParam struct {
|
|||
|
||||
type KeySummary struct {
|
||||
Type string `json:"type"`
|
||||
TTL int64 `json:"ttl"`
|
||||
Size int64 `json:"size"`
|
||||
Length int64 `json:"length"`
|
||||
TTL int64 `json:"ttl,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Length int64 `json:"length,omitempty"`
|
||||
}
|
||||
|
||||
type KeyDetailParam struct {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import ContentPane from './components/content/ContentPane.vue'
|
||||
import BrowserPane from './components/sidebar/BrowserPane.vue'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { debounce, get } from 'lodash'
|
||||
import { debounce } from 'lodash'
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
import Ribbon from './components/sidebar/Ribbon.vue'
|
||||
import ConnectionPane from './components/sidebar/ConnectionPane.vue'
|
||||
|
@ -139,7 +139,7 @@ onMounted(async () => {
|
|||
<div style="min-width: 68px; font-weight: 800">Tiny RDM</div>
|
||||
<transition name="fade">
|
||||
<n-text v-if="tabStore.nav === 'browser'" class="ellipsis" strong style="font-size: 13px">
|
||||
- {{ get(tabStore.currentTab, 'name') }}
|
||||
- {{ tabStore.currentTabName }}
|
||||
</n-text>
|
||||
</transition>
|
||||
</n-space>
|
||||
|
@ -174,13 +174,15 @@ onMounted(async () => {
|
|||
@update:size="handleResize">
|
||||
<browser-pane
|
||||
v-for="t in tabStore.tabs"
|
||||
v-show="get(tabStore.currentTab, 'name') === t.name"
|
||||
v-show="tabStore.currentTabName === t.name"
|
||||
:key="t.name"
|
||||
:db="t.db"
|
||||
:server="t.name"
|
||||
class="app-side flex-item-expand" />
|
||||
</resizeable-wrapper>
|
||||
<content-pane
|
||||
v-for="t in tabStore.tabs"
|
||||
v-show="get(tabStore.currentTab, 'name') === t.name"
|
||||
v-show="tabStore.currentTabName === t.name"
|
||||
:key="t.name"
|
||||
:server="t.name"
|
||||
class="flex-item-expand" />
|
||||
|
|
|
@ -3,6 +3,7 @@ import { computed, h } from 'vue'
|
|||
import { useThemeVars } from 'naive-ui'
|
||||
import { types, typesBgColor, typesColor } from '@/consts/support_redis_type.js'
|
||||
import { get, map, toUpper } from 'lodash'
|
||||
import RedisTypeTag from '@/components/common/RedisTypeTag.vue'
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
|
@ -23,15 +24,24 @@ const options = computed(() => {
|
|||
|
||||
const themeVars = useThemeVars()
|
||||
const renderIcon = (option) => {
|
||||
if (option.key === props.value) {
|
||||
const backgroundColor = get(typesColor, option.key, themeVars.value.textColor3)
|
||||
return h('div', { style: { borderRadius: '999px', width: '10px', height: '10px', backgroundColor } }, '')
|
||||
}
|
||||
return h(RedisTypeTag, {
|
||||
type: option.key,
|
||||
short: true,
|
||||
size: 'small',
|
||||
inverse: option.key === props.value,
|
||||
})
|
||||
}
|
||||
|
||||
const renderLabel = (option) => {
|
||||
const color = get(typesColor, option.key, '')
|
||||
return h('div', { style: { color, fontWeight: '450' } }, option.label)
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
fontWeight: option.key === props.value ? 'bold' : 'normal',
|
||||
},
|
||||
},
|
||||
option.label,
|
||||
)
|
||||
}
|
||||
|
||||
const fontColor = computed(() => {
|
||||
|
|
|
@ -1,45 +1,69 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { typesBgColor, typesColor, validType } from '@/consts/support_redis_type.js'
|
||||
import { typesBgColor, typesColor, typesShortName } from '@/consts/support_redis_type.js'
|
||||
import Binary from '@/components/icons/Binary.vue'
|
||||
import { toUpper } from 'lodash'
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
return validType(value)
|
||||
},
|
||||
default: 'STRING',
|
||||
},
|
||||
binaryKey: Boolean,
|
||||
size: String,
|
||||
short: Boolean,
|
||||
round: Boolean,
|
||||
inverse: Boolean,
|
||||
})
|
||||
|
||||
const fontColor = computed(() => {
|
||||
return typesColor[props.type]
|
||||
if (props.inverse) {
|
||||
return typesBgColor[props.type]
|
||||
} else {
|
||||
return typesColor[props.type]
|
||||
}
|
||||
})
|
||||
|
||||
const backgroundColor = computed(() => {
|
||||
return typesBgColor[props.type]
|
||||
if (props.inverse) {
|
||||
return typesColor[props.type]
|
||||
} else {
|
||||
return typesBgColor[props.type]
|
||||
}
|
||||
})
|
||||
|
||||
const label = computed(() => {
|
||||
if (props.short) {
|
||||
return typesShortName[toUpper(props.type)] || 'N'
|
||||
}
|
||||
return toUpper(props.type)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tag
|
||||
:class="[props.size === 'small' ? 'redis-type-tag-small' : 'redis-type-tag']"
|
||||
:class="{
|
||||
'redis-type-tag-normal': !props.short && props.size !== 'small',
|
||||
'redis-type-tag-small': !props.short && props.size === 'small',
|
||||
'redis-type-tag-round': props.round,
|
||||
}"
|
||||
:color="{ color: backgroundColor, textColor: fontColor }"
|
||||
:size="props.size"
|
||||
bordered
|
||||
strong>
|
||||
{{ props.type }}
|
||||
<b>{{ label }}</b>
|
||||
<template #icon>
|
||||
<n-icon v-if="binaryKey" :component="Binary" size="18" />
|
||||
</template>
|
||||
</n-tag>
|
||||
<!-- <div class="redis-type-tag flex-box-h" :style="{backgroundColor: backgroundColor}">{{ props.type }}</div>-->
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.redis-type-tag {
|
||||
.redis-type-tag-round {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.redis-type-tag-normal {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ const cleanHistory = async () => {
|
|||
if (success) {
|
||||
data.history = []
|
||||
tableRef.value?.scrollTo({ top: 0 })
|
||||
$message.success(i18n.t('common.success'))
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
}
|
||||
} finally {
|
||||
data.loading = false
|
||||
|
|
|
@ -87,7 +87,7 @@ const renderTypeLabel = (option) => {
|
|||
default: () => [
|
||||
h('div', {
|
||||
style: {
|
||||
borderRadius: '50%',
|
||||
borderRadius: '9999px',
|
||||
backgroundColor: typesColor[option.value],
|
||||
width: '13px',
|
||||
height: '13px',
|
||||
|
|
|
@ -3,8 +3,8 @@ import { useThemeVars } from 'naive-ui'
|
|||
import BrowserTree from './BrowserTree.vue'
|
||||
import IconButton from '@/components/common/IconButton.vue'
|
||||
import useTabStore from 'stores/tab.js'
|
||||
import { computed, nextTick, onMounted, reactive, ref, unref, watch } from 'vue'
|
||||
import { find, get, map } from 'lodash'
|
||||
import { computed, nextTick, onMounted, reactive, ref, unref } from 'vue'
|
||||
import { find, map } from 'lodash'
|
||||
import Refresh from '@/components/icons/Refresh.vue'
|
||||
import useDialogStore from 'stores/dialog.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -24,29 +24,31 @@ import ListCheckbox from '@/components/icons/ListCheckbox.vue'
|
|||
import Close from '@/components/icons/Close.vue'
|
||||
import More from '@/components/icons/More.vue'
|
||||
|
||||
const props = defineProps({
|
||||
server: String,
|
||||
db: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const themeVars = useThemeVars()
|
||||
const i18n = useI18n()
|
||||
const dialogStore = useDialogStore()
|
||||
// const prefStore = usePreferencesStore()
|
||||
const tabStore = useTabStore()
|
||||
const browserStore = useBrowserStore()
|
||||
const connectionStore = useConnectionStore()
|
||||
const render = useRender()
|
||||
const currentName = computed(() => get(tabStore.currentTab, 'name', ''))
|
||||
const browserTreeRef = ref(null)
|
||||
const loading = ref(false)
|
||||
const fullyLoaded = ref(false)
|
||||
const inCheckState = ref(false)
|
||||
const checkedCount = ref(0)
|
||||
|
||||
const selectedDB = computed(() => {
|
||||
return browserStore.selectedDatabases[currentName.value] || 0
|
||||
})
|
||||
|
||||
const dbSelectOptions = computed(() => {
|
||||
const dblist = browserStore.getDBList(currentName.value)
|
||||
const dblist = browserStore.getDBList(props.server)
|
||||
return map(dblist, (db) => {
|
||||
if (selectedDB.value === db.db) {
|
||||
if (props.db === db.db) {
|
||||
return {
|
||||
value: db.db,
|
||||
label: `db${db.db} (${db.keys}/${db.maxKeys})`,
|
||||
|
@ -71,7 +73,7 @@ const moreOptions = computed(() => {
|
|||
})
|
||||
|
||||
const loadProgress = computed(() => {
|
||||
const db = browserStore.getDatabase(currentName.value, selectedDB.value)
|
||||
const db = browserStore.getDatabase(props.server, props.db)
|
||||
if (db.maxKeys <= 0) {
|
||||
return 100
|
||||
}
|
||||
|
@ -79,29 +81,29 @@ const loadProgress = computed(() => {
|
|||
})
|
||||
|
||||
const checkedTip = computed(() => {
|
||||
const dblist = browserStore.getDBList(currentName.value)
|
||||
const db = find(dblist, { db: selectedDB.value })
|
||||
const dblist = browserStore.getDBList(props.server)
|
||||
const db = find(dblist, { db: props.db })
|
||||
return `${checkedCount.value} / ${db.maxKeys}`
|
||||
})
|
||||
|
||||
const onReload = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
tabStore.setSelectedKeys(currentName.value)
|
||||
const db = selectedDB.value
|
||||
browserStore.closeDatabase(currentName.value, db)
|
||||
browserTreeRef.value?.resetExpandKey(currentName.value, db)
|
||||
tabStore.setSelectedKeys(props.server)
|
||||
const db = props.db
|
||||
browserStore.closeDatabase(props.server, db)
|
||||
browserTreeRef.value?.resetExpandKey(props.server, db)
|
||||
|
||||
let matchType = unref(filterForm.type)
|
||||
if (!types.hasOwnProperty(matchType)) {
|
||||
matchType = ''
|
||||
}
|
||||
browserStore.setKeyFilter(currentName.value, {
|
||||
browserStore.setKeyFilter(props.server, {
|
||||
type: matchType,
|
||||
pattern: unref(filterForm.pattern),
|
||||
})
|
||||
await browserStore.openDatabase(currentName.value, db)
|
||||
fullyLoaded.value = await browserStore.loadMoreKeys(currentName.value, db)
|
||||
await browserStore.openDatabase(props.server, db)
|
||||
fullyLoaded.value = await browserStore.loadMoreKeys(props.server, db)
|
||||
// $message.success(i18n.t('dialogue.reload_succ'))
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
|
@ -111,13 +113,13 @@ const onReload = async () => {
|
|||
}
|
||||
|
||||
const onAddKey = () => {
|
||||
dialogStore.openNewKeyDialog('', currentName.value, selectedDB.value)
|
||||
dialogStore.openNewKeyDialog('', props.server, props.db)
|
||||
}
|
||||
|
||||
const onLoadMore = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
fullyLoaded.value = await browserStore.loadMoreKeys(currentName.value, selectedDB.value)
|
||||
fullyLoaded.value = await browserStore.loadMoreKeys(props.server, props.db)
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
} finally {
|
||||
|
@ -128,7 +130,7 @@ const onLoadMore = async () => {
|
|||
const onLoadAll = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
await browserStore.loadAllKeys(currentName.value, selectedDB.value)
|
||||
await browserStore.loadAllKeys(props.server, props.db)
|
||||
fullyLoaded.value = true
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
|
@ -142,15 +144,30 @@ const onDeleteChecked = () => {
|
|||
}
|
||||
|
||||
const onFlush = () => {
|
||||
dialogStore.openFlushDBDialog(currentName.value, selectedDB.value)
|
||||
dialogStore.openFlushDBDialog(props.server, props.db)
|
||||
}
|
||||
|
||||
const onDisconnect = () => {
|
||||
browserStore.closeConnection(currentName.value)
|
||||
browserStore.closeConnection(props.server)
|
||||
}
|
||||
|
||||
const handleSelectDB = async (db, prevDB) => {
|
||||
// watch 'browserStore.openedDB[currentName.value]' instead
|
||||
const handleSelectDB = async (db) => {
|
||||
try {
|
||||
loading.value = true
|
||||
browserStore.closeDatabase(props.server, props.db)
|
||||
browserStore.setKeyFilter(props.server, {})
|
||||
await browserStore.openDatabase(props.server, db)
|
||||
await nextTick()
|
||||
await connectionStore.saveLastDB(props.server, db)
|
||||
tabStore.upsertTab({ server: props.server, db })
|
||||
// browserTreeRef.value?.resetExpandKey(props.server, db)
|
||||
fullyLoaded.value = await browserStore.loadMoreKeys(props.server, db)
|
||||
browserTreeRef.value?.refreshTree()
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const filterForm = reactive({
|
||||
|
@ -183,31 +200,6 @@ const onSelectOptions = (select) => {
|
|||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => browserStore.openedDB[currentName.value],
|
||||
async (db, prevDB) => {
|
||||
if (db === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
browserStore.closeDatabase(currentName.value, prevDB)
|
||||
browserStore.setKeyFilter(currentName.value, {})
|
||||
await browserStore.openDatabase(currentName.value, db)
|
||||
// browserTreeRef.value?.resetExpandKey(currentName.value, db)
|
||||
fullyLoaded.value = await browserStore.loadMoreKeys(currentName.value, db)
|
||||
browserTreeRef.value?.refreshTree()
|
||||
|
||||
nextTick().then(() => connectionStore.saveLastDB(currentName.value, db))
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => onReload())
|
||||
// forbid dynamic switch key view due to performance issues
|
||||
// const viewType = ref(0)
|
||||
|
@ -220,7 +212,7 @@ onMounted(() => onReload())
|
|||
<template>
|
||||
<div class="nav-pane-container flex-box-v">
|
||||
<!-- top function bar -->
|
||||
<div class="flex-box-h nav-pane-func">
|
||||
<div class="flex-box-h nav-pane-func" style="height: 36px">
|
||||
<content-search-input
|
||||
:debounce-wait="1000"
|
||||
:full-search-icon="Search"
|
||||
|
@ -271,11 +263,11 @@ onMounted(() => onReload())
|
|||
ref="browserTreeRef"
|
||||
v-model:checked-count="checkedCount"
|
||||
:check-mode="inCheckState"
|
||||
:db="browserStore.openedDB[currentName]"
|
||||
:db="props.db"
|
||||
:full-loaded="fullyLoaded"
|
||||
:loading="loading && loadProgress <= 0"
|
||||
:pattern="filterForm.filter"
|
||||
:server="currentName" />
|
||||
:server="props.server" />
|
||||
<!-- bottom function bar -->
|
||||
<div class="nav-pane-bottom flex-box-v">
|
||||
<!-- <switch-button-->
|
||||
|
@ -288,10 +280,10 @@ onMounted(() => onReload())
|
|||
<transition mode="out-in" name="fade">
|
||||
<div v-if="!inCheckState" class="flex-box-h nav-pane-func">
|
||||
<n-select
|
||||
v-model:value="browserStore.openedDB[currentName]"
|
||||
:consistent-menu-width="false"
|
||||
:filter="(pattern, option) => option.value.toString() === pattern"
|
||||
:options="dbSelectOptions"
|
||||
:value="props.db"
|
||||
filterable
|
||||
size="small"
|
||||
style="min-width: 100px; max-width: 200px"
|
||||
|
@ -372,16 +364,6 @@ onMounted(() => onReload())
|
|||
border-color: #0000;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nav-pane-top {
|
||||
//@include bottom-shadow(0.1);
|
||||
color: v-bind('themeVars.iconColor');
|
||||
|
|
|
@ -5,7 +5,7 @@ import { NIcon, NSpace, useThemeVars } from 'naive-ui'
|
|||
import Key from '@/components/icons/Key.vue'
|
||||
import Binary from '@/components/icons/Binary.vue'
|
||||
import Database from '@/components/icons/Database.vue'
|
||||
import { filter, find, get, includes, indexOf, isEmpty, map, remove, size, startsWith } from 'lodash'
|
||||
import { filter, find, get, includes, indexOf, isEmpty, map, remove, size, startsWith, toUpper } from 'lodash'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Refresh from '@/components/icons/Refresh.vue'
|
||||
import CopyLink from '@/components/icons/CopyLink.vue'
|
||||
|
@ -23,6 +23,7 @@ import LoadList from '@/components/icons/LoadList.vue'
|
|||
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import { useRender } from '@/utils/render.js'
|
||||
import RedisTypeTag from '@/components/common/RedisTypeTag.vue'
|
||||
|
||||
const props = defineProps({
|
||||
server: String,
|
||||
|
@ -364,13 +365,28 @@ const renderPrefix = ({ option }) => {
|
|||
},
|
||||
)
|
||||
case ConnectionType.RedisValue:
|
||||
return h(
|
||||
NIcon,
|
||||
{ size: 20 },
|
||||
{
|
||||
default: () => h(!!option.redisKeyCode ? Binary : Key),
|
||||
},
|
||||
)
|
||||
if (option.redisType == null || option.redisType === 'loading') {
|
||||
browserStore.loadKeyType({
|
||||
server: props.server,
|
||||
db: option.db,
|
||||
key: option.redisKey,
|
||||
keyCode: option.redisKeyCode,
|
||||
})
|
||||
// in loading
|
||||
return h(
|
||||
NIcon,
|
||||
{ size: 20 },
|
||||
{
|
||||
default: () => h(!!option.redisKeyCode ? Binary : Key),
|
||||
},
|
||||
)
|
||||
}
|
||||
return h(RedisTypeTag, {
|
||||
type: toUpper(option.redisType),
|
||||
short: true,
|
||||
size: 'small',
|
||||
inverse: includes(selectedKeys.value, option.key),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -301,6 +301,7 @@ const openConnection = async (name) => {
|
|||
if (!isEmpty(connectingServer.value)) {
|
||||
tabStore.upsertTab({
|
||||
server: name,
|
||||
db: browserStore.getSelectedDB(name),
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -11,6 +11,15 @@ export const types = {
|
|||
STREAM: 'STREAM',
|
||||
}
|
||||
|
||||
export const typesShortName = {
|
||||
STRING: 'S',
|
||||
HASH: 'H',
|
||||
LIST: 'L',
|
||||
SET: 'S',
|
||||
ZSET: 'Z',
|
||||
STREAM: 'X',
|
||||
}
|
||||
|
||||
/**
|
||||
* mark color for redis types
|
||||
* @enum {string}
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
GetCmdHistory,
|
||||
GetKeyDetail,
|
||||
GetKeySummary,
|
||||
GetKeyType,
|
||||
GetSlowLogs,
|
||||
LoadAllKeys,
|
||||
LoadNextKeys,
|
||||
|
@ -72,6 +73,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only
|
||||
* @property {boolean} [expanded] - current node is expanded
|
||||
* @property {DatabaseItem[]} [children]
|
||||
* @property {string} [redisType] - redis type name, 'loading' indicate that is in loading progress
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -112,7 +114,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
filter: {}, // all filters in opened connections map by server and FilterItem
|
||||
loadingState: {}, // all loading state in opened connections map by server and LoadingState
|
||||
viewType: {}, // view type selection for all opened connections group by 'server'
|
||||
databases: {}, // all databases in opened connections group by 'server name'
|
||||
databases: {}, // all database lists in opened connections group by 'server name'
|
||||
nodeMap: {}, // all nodes in opened connections group by 'server#db' and 'type/key'
|
||||
keySet: {}, // all keys set in opened connections group by 'server#db
|
||||
openedDB: {}, // opened database map by server and database index
|
||||
|
@ -339,6 +341,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
|
||||
selDB.opened = true
|
||||
selDB.maxKeys = maxKeys
|
||||
this.openedDB[server] = db
|
||||
set(this.loadingState, 'fullLoaded', end)
|
||||
if (isEmpty(keys)) {
|
||||
selDB.children = []
|
||||
|
@ -465,6 +468,29 @@ const useBrowserStore = defineStore('browser', {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* load key type
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number[]} keyCode
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async loadKeyType({ server, db, key, keyCode }) {
|
||||
try {
|
||||
const nodeMap = this._getNodeMap(server, db)
|
||||
const node = nodeMap.get(`${ConnectionType.RedisValue}/${key}`)
|
||||
if (node == null || node.redisType != null) {
|
||||
return
|
||||
}
|
||||
node.redisType = 'loading'
|
||||
const { data } = await GetKeyType({ server, db, key: keyCode || key })
|
||||
const { type } = data || {}
|
||||
node.redisType = type
|
||||
} finally {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* reload key
|
||||
* @param {string} server
|
||||
|
@ -752,6 +778,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
keys: 0,
|
||||
redisKey: k,
|
||||
redisKeyCode: isBinaryKey ? key : undefined,
|
||||
redisKeyType: undefined,
|
||||
type: ConnectionType.RedisValue,
|
||||
isLeaf: true,
|
||||
}
|
||||
|
@ -818,6 +845,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
redisKeyCode: isBinaryKey ? key : undefined,
|
||||
redisKeyType: undefined,
|
||||
type: ConnectionType.RedisValue,
|
||||
isLeaf: true,
|
||||
}
|
||||
|
|
|
@ -119,6 +119,10 @@ const useTabStore = defineStore('tab', {
|
|||
// return current
|
||||
},
|
||||
|
||||
currentTabName() {
|
||||
return get(this.tabs, [this.activatedIndex, 'name'])
|
||||
},
|
||||
|
||||
currentSelectedKeys() {
|
||||
const tab = this.currentTab()
|
||||
return get(tab, 'selectedKeys', [])
|
||||
|
|
|
@ -157,3 +157,14 @@ body {
|
|||
.n-tabs .n-tabs-nav {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
// animations
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue