Compare commits
6 Commits
34a0be4d08
...
acd3fa9304
Author | SHA1 | Date |
---|---|---|
tiny-craft | acd3fa9304 | |
tiny-craft | f9fe74a6b4 | |
tiny-craft | 92ffd6c605 | |
tiny-craft | c4e4ed7e79 | |
tiny-craft | 5bbd87cc31 | |
tiny-craft | a669f3dfcb |
|
@ -508,6 +508,7 @@ func (c *connectionService) OpenConnection(name string) (resp types.JSResp) {
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Data = map[string]any{
|
resp.Data = map[string]any{
|
||||||
"db": dbs,
|
"db": dbs,
|
||||||
|
"view": selConn.KeyView,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -770,7 +771,8 @@ func (c *connectionService) LoadAllKeys(connName string, db int, match, keyType
|
||||||
}
|
}
|
||||||
|
|
||||||
client, ctx := item.client, item.ctx
|
client, ctx := item.client, item.ctx
|
||||||
keys, _, err := c.scanKeys(ctx, client, match, keyType, 0, 0)
|
cursor := item.cursor[db]
|
||||||
|
keys, _, err := c.scanKeys(ctx, client, match, keyType, cursor, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
|
|
@ -15,6 +15,7 @@ type ConnectionConfig struct {
|
||||||
ExecTimeout int `json:"execTimeout,omitempty" yaml:"exec_timeout,omitempty"`
|
ExecTimeout int `json:"execTimeout,omitempty" yaml:"exec_timeout,omitempty"`
|
||||||
DBFilterType string `json:"dbFilterType" yaml:"db_filter_type,omitempty"`
|
DBFilterType string `json:"dbFilterType" yaml:"db_filter_type,omitempty"`
|
||||||
DBFilterList []int `json:"dbFilterList" yaml:"db_filter_list,omitempty"`
|
DBFilterList []int `json:"dbFilterList" yaml:"db_filter_list,omitempty"`
|
||||||
|
KeyView int `json:"keyView,omitempty" yaml:"key_view,omitempty"`
|
||||||
LoadSize int `json:"loadSize,omitempty" yaml:"load_size,omitempty"`
|
LoadSize int `json:"loadSize,omitempty" yaml:"load_size,omitempty"`
|
||||||
MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"`
|
MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"`
|
||||||
SSL ConnectionSSL `json:"ssl,omitempty" yaml:"ssl,omitempty"`
|
SSL ConnectionSSL `json:"ssl,omitempty" yaml:"ssl,omitempty"`
|
||||||
|
|
|
@ -24,6 +24,10 @@ const props = defineProps({
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
default: 3,
|
default: 3,
|
||||||
},
|
},
|
||||||
|
unselectStrokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:value'])
|
const emit = defineEmits(['update:value'])
|
||||||
|
@ -46,7 +50,9 @@ const handleSwitch = (idx) => {
|
||||||
<n-button :tertiary="i !== props.value" :focusable="false" :size="props.size" @click="handleSwitch(i)">
|
<n-button :tertiary="i !== props.value" :focusable="false" :size="props.size" @click="handleSwitch(i)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :size="props.iconSize">
|
<n-icon :size="props.iconSize">
|
||||||
<component :is="icon" :stroke-width="props.strokeWidth" />
|
<component
|
||||||
|
:is="icon"
|
||||||
|
:stroke-width="i !== props.value ? props.unselectStrokeWidth : props.strokeWidth" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
</n-button>
|
</n-button>
|
||||||
|
|
|
@ -7,6 +7,9 @@ import { map, uniqBy } from 'lodash'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import Delete from '@/components/icons/Delete.vue'
|
import Delete from '@/components/icons/Delete.vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
|
||||||
|
const themeVars = useThemeVars()
|
||||||
|
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
|
@ -72,7 +75,8 @@ defineExpose({
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
: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;">
|
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
|
||||||
|
@ -158,5 +162,6 @@ defineExpose({
|
||||||
.content-container {
|
.content-container {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
border-left: 1px solid v-bind('themeVars.borderColor');
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -25,20 +25,20 @@ const activeTabStyle = computed(() => {
|
||||||
const { name } = tabStore.currentTab
|
const { name } = tabStore.currentTab
|
||||||
const { markColor = '' } = connectionStore.serverProfile[name] || {}
|
const { markColor = '' } = connectionStore.serverProfile[name] || {}
|
||||||
return {
|
return {
|
||||||
backgroundColor: themeVars.value.bodyColor,
|
|
||||||
borderTopWidth: markColor ? '3px' : '1px',
|
borderTopWidth: markColor ? '3px' : '1px',
|
||||||
borderTopColor: markColor || themeVars.value.borderColor,
|
borderTopColor: markColor || themeVars.value.borderColor,
|
||||||
borderBottomColor: themeVars.value.bodyColor,
|
|
||||||
borderTopLeftRadius: themeVars.value.borderRadius,
|
|
||||||
borderTopRightRadius: themeVars.value.borderRadius,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const inactiveTabStyle = computed(() => ({
|
|
||||||
borderWidth: '0 0 1px',
|
const tabClass = (idx) => {
|
||||||
// borderBottomColor: themeVars.value.borderColor,
|
if (tabStore.activatedIndex === idx) {
|
||||||
borderTopLeftRadius: themeVars.value.borderRadius,
|
return ['value-tab', 'value-tab-active']
|
||||||
borderTopRightRadius: themeVars.value.borderRadius,
|
} else if (tabStore.activatedIndex - 1 === idx) {
|
||||||
}))
|
return ['value-tab', 'value-tab-inactive']
|
||||||
|
} else {
|
||||||
|
return ['value-tab', 'value-tab-inactive', 'value-tab-inactive2']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tab = computed(() =>
|
const tab = computed(() =>
|
||||||
map(tabStore.tabs, (item) => ({
|
map(tabStore.tabs, (item) => ({
|
||||||
|
@ -52,6 +52,7 @@ const tab = computed(() =>
|
||||||
<n-tabs
|
<n-tabs
|
||||||
v-model:value="tabStore.activatedIndex"
|
v-model:value="tabStore.activatedIndex"
|
||||||
:closable="true"
|
:closable="true"
|
||||||
|
:tabs-padding="2"
|
||||||
:tab-style="{
|
:tab-style="{
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
|
@ -60,7 +61,6 @@ const tab = computed(() =>
|
||||||
}"
|
}"
|
||||||
:theme-overrides="{
|
:theme-overrides="{
|
||||||
tabFontWeightActive: 800,
|
tabFontWeightActive: 800,
|
||||||
tabBorderRadius: 0,
|
|
||||||
tabGapSmallCard: 0,
|
tabGapSmallCard: 0,
|
||||||
tabGapMediumCard: 0,
|
tabGapMediumCard: 0,
|
||||||
tabGapLargeCard: 0,
|
tabGapLargeCard: 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"
|
||||||
:closable="tabStore.activatedIndex === i"
|
:closable="true"
|
||||||
:name="i"
|
:name="i"
|
||||||
:style="tabStore.activatedIndex === i ? activeTabStyle : inactiveTabStyle"
|
:style="tabStore.activatedIndex === i ? activeTabStyle : undefined"
|
||||||
style="--wails-draggable: none"
|
: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 :component="ToggleServer" size="18" />
|
<n-icon :component="ToggleServer" size="18" />
|
||||||
|
@ -89,4 +89,38 @@ const tab = computed(() =>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss">
|
||||||
|
.value-tab {
|
||||||
|
--wails-draggable: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-tab-active {
|
||||||
|
background-color: v-bind('themeVars.bodyColor') !important;
|
||||||
|
border-bottom-color: v-bind('themeVars.bodyColor') !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-tab-inactive {
|
||||||
|
border-color: #0000 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: v-bind('themeVars.borderColor') !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-tab-inactive2 {
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 25%;
|
||||||
|
height: 50%;
|
||||||
|
width: 1px;
|
||||||
|
background-color: v-bind('themeVars.borderColor');
|
||||||
|
right: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
background-color: #0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import useDialog, { ConnDialogType } from 'stores/dialog'
|
||||||
import Close from '@/components/icons/Close.vue'
|
import Close from '@/components/icons/Close.vue'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
||||||
|
import { KeyViewType } from '@/consts/key_view_type.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog for new or edit connection
|
* Dialog for new or edit connection
|
||||||
|
@ -258,15 +259,24 @@ const onClose = () => {
|
||||||
:rules="generalFormRules()"
|
:rules="generalFormRules()"
|
||||||
:show-require-mark="false"
|
:show-require-mark="false"
|
||||||
label-placement="top">
|
label-placement="top">
|
||||||
<n-form-item :label="$t('dialogue.connection.conn_name')" path="name" required>
|
<n-grid :x-gap="10">
|
||||||
|
<n-form-item-gi
|
||||||
|
:label="$t('dialogue.connection.conn_name')"
|
||||||
|
:span="24"
|
||||||
|
path="name"
|
||||||
|
required>
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="generalForm.name"
|
v-model:value="generalForm.name"
|
||||||
:placeholder="$t('dialogue.connection.name_tip')" />
|
:placeholder="$t('dialogue.connection.name_tip')" />
|
||||||
</n-form-item>
|
</n-form-item-gi>
|
||||||
<n-form-item v-if="!isEditMode" :label="$t('dialogue.connection.group')" required>
|
<n-form-item-gi
|
||||||
|
v-if="!isEditMode"
|
||||||
|
:label="$t('dialogue.connection.group')"
|
||||||
|
:span="24"
|
||||||
|
required>
|
||||||
<n-select v-model:value="generalForm.group" :options="groupOptions" />
|
<n-select v-model:value="generalForm.group" :options="groupOptions" />
|
||||||
</n-form-item>
|
</n-form-item-gi>
|
||||||
<n-form-item :label="$t('dialogue.connection.addr')" path="addr" required>
|
<n-form-item-gi :label="$t('dialogue.connection.addr')" :span="24" path="addr" required>
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="generalForm.addr"
|
v-model:value="generalForm.addr"
|
||||||
:placeholder="$t('dialogue.connection.addr_tip')" />
|
:placeholder="$t('dialogue.connection.addr_tip')" />
|
||||||
|
@ -276,19 +286,20 @@ const onClose = () => {
|
||||||
:max="65535"
|
:max="65535"
|
||||||
:min="1"
|
:min="1"
|
||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</n-form-item>
|
</n-form-item-gi>
|
||||||
<n-form-item :label="$t('dialogue.connection.pwd')" path="password">
|
<n-form-item-gi :label="$t('dialogue.connection.pwd')" :span="12" path="password">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="generalForm.password"
|
v-model:value="generalForm.password"
|
||||||
:placeholder="$t('dialogue.connection.pwd_tip')"
|
:placeholder="$t('dialogue.connection.pwd_tip')"
|
||||||
show-password-on="click"
|
show-password-on="click"
|
||||||
type="password" />
|
type="password" />
|
||||||
</n-form-item>
|
</n-form-item-gi>
|
||||||
<n-form-item :label="$t('dialogue.connection.usr')" path="username">
|
<n-form-item-gi :label="$t('dialogue.connection.usr')" :span="12" path="username">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="generalForm.username"
|
v-model:value="generalForm.username"
|
||||||
:placeholder="$t('dialogue.connection.usr_tip')" />
|
:placeholder="$t('dialogue.connection.usr_tip')" />
|
||||||
</n-form-item>
|
</n-form-item-gi>
|
||||||
|
</n-grid>
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
|
@ -337,6 +348,16 @@ const onClose = () => {
|
||||||
</template>
|
</template>
|
||||||
</n-input-number>
|
</n-input-number>
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
|
<n-form-item-gi :label="$t('dialogue.connection.advn.key_view')" :span="12">
|
||||||
|
<n-radio-group v-model:value="generalForm.keyView">
|
||||||
|
<n-radio-button
|
||||||
|
:label="$t('dialogue.connection.advn.key_view_tree')"
|
||||||
|
:value="KeyViewType.Tree" />
|
||||||
|
<n-radio-button
|
||||||
|
:label="$t('dialogue.connection.advn.key_view_list')"
|
||||||
|
:value="KeyViewType.List" />
|
||||||
|
</n-radio-group>
|
||||||
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :label="$t('dialogue.connection.advn.load_size')" :span="12">
|
<n-form-item-gi :label="$t('dialogue.connection.advn.load_size')" :span="12">
|
||||||
<n-input-number v-model:value="generalForm.loadSize" :min="0" />
|
<n-input-number v-model:value="generalForm.loadSize" :min="0" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
|
@ -403,20 +424,20 @@ const onClose = () => {
|
||||||
<n-form-item :label="$t('dialogue.connection.ssl.cert_file')">
|
<n-form-item :label="$t('dialogue.connection.ssl.cert_file')">
|
||||||
<file-open-input
|
<file-open-input
|
||||||
v-model:value="generalForm.ssl.certFile"
|
v-model:value="generalForm.ssl.certFile"
|
||||||
:placeholder="$t('dialogue.connection.ssl.cert_file_tip')"
|
:disabled="!generalForm.ssl.enable"
|
||||||
:disabled="!generalForm.ssl.enable" />
|
:placeholder="$t('dialogue.connection.ssl.cert_file_tip')" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('dialogue.connection.ssl.key_file')">
|
<n-form-item :label="$t('dialogue.connection.ssl.key_file')">
|
||||||
<file-open-input
|
<file-open-input
|
||||||
v-model:value="generalForm.ssl.keyFile"
|
v-model:value="generalForm.ssl.keyFile"
|
||||||
:placeholder="$t('dialogue.connection.ssl.key_file_tip')"
|
:disabled="!generalForm.ssl.enable"
|
||||||
:disabled="!generalForm.ssl.enable" />
|
:placeholder="$t('dialogue.connection.ssl.key_file_tip')" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('dialogue.connection.ssl.ca_file')">
|
<n-form-item :label="$t('dialogue.connection.ssl.ca_file')">
|
||||||
<file-open-input
|
<file-open-input
|
||||||
v-model:value="generalForm.ssl.caFile"
|
v-model:value="generalForm.ssl.caFile"
|
||||||
:placeholder="$t('dialogue.connection.ssl.ca_file_tip')"
|
:disabled="!generalForm.ssl.enable"
|
||||||
:disabled="!generalForm.ssl.enable" />
|
:placeholder="$t('dialogue.connection.ssl.ca_file_tip')" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
@ -467,8 +488,8 @@ const onClose = () => {
|
||||||
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.pkfile')">
|
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.pkfile')">
|
||||||
<file-open-input
|
<file-open-input
|
||||||
v-model:value="generalForm.ssh.pkFile"
|
v-model:value="generalForm.ssh.pkFile"
|
||||||
:placeholder="$t('dialogue.connection.ssh.pkfile_tip')"
|
:disabled="!generalForm.ssh.enable"
|
||||||
:disabled="!generalForm.ssh.enable" />
|
:placeholder="$t('dialogue.connection.ssh.pkfile_tip')" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.passphrase')">
|
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.passphrase')">
|
||||||
<n-input
|
<n-input
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M9 42C11.2091 42 13 40.2091 13 38C13 35.7909 11.2091 34 9 34C6.79086 34 5 35.7909 5 38C5 40.2091 6.79086 42 9 42Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M9 14C11.2091 14 13 12.2092 13 10C13 7.79086 11.2091 6 9 6C6.79086 6 5 7.79086 5 10C5 12.2092 6.79086 14 9 14Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M9 28C11.2091 28 13 26.2092 13 24C13 21.7908 11.2091 20 9 20C6.79086 20 5 21.7908 5 24C5 26.2092 6.79086 28 9 28Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M21 24H43"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M21 38H43"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M21 10H43"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M38 20H18V28H38V20Z"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M32 6H18V14H32V6Z"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M44 34H18V42H44V34Z"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M17 10H5"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M17 24H5"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M17 38H5"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M5 44V4"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { NIcon, useThemeVars } from 'naive-ui'
|
||||||
import BrowserTree from './BrowserTree.vue'
|
import BrowserTree from './BrowserTree.vue'
|
||||||
import IconButton from '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
import useTabStore from 'stores/tab.js'
|
import useTabStore from 'stores/tab.js'
|
||||||
|
@ -13,6 +13,9 @@ 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()
|
||||||
|
@ -59,6 +62,13 @@ const filterTypeOptions = computed(() => {
|
||||||
})
|
})
|
||||||
return options
|
return options
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// forbid dynamic switch key view due to performance issues
|
||||||
|
// const viewType = ref(0)
|
||||||
|
// const onSwitchView = (selectView) => {
|
||||||
|
// const { server } = tabStore.currentTab
|
||||||
|
// connectionStore.switchKeyView(server, selectView)
|
||||||
|
// }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -82,9 +92,16 @@ const filterTypeOptions = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
<!-- bottom function bar -->
|
<!-- bottom function bar -->
|
||||||
<div class="nav-pane-bottom flex-box-h">
|
<div class="nav-pane-bottom flex-box-h">
|
||||||
|
<!-- <switch-button-->
|
||||||
|
<!-- v-model:value="viewType"-->
|
||||||
|
<!-- :icons="[TreeView, ListView]"-->
|
||||||
|
<!-- :t-tooltips="['interface.tree_view', 'interface.list_view']"-->
|
||||||
|
<!-- stroke-width="4"-->
|
||||||
|
<!-- unselect-stroke-width="3"-->
|
||||||
|
<!-- @update:value="onSwitchView" />-->
|
||||||
<icon-button :icon="Status" size="20" stroke-width="4" t-tooltip="interface.status" @click="onInfo" />
|
<icon-button :icon="Status" size="20" stroke-width="4" t-tooltip="interface.status" @click="onInfo" />
|
||||||
<icon-button :icon="Refresh" size="20" stroke-width="4" t-tooltip="interface.reload" @click="onRefresh" />
|
<icon-button :icon="Refresh" size="20" stroke-width="4" t-tooltip="interface.reload" @click="onRefresh" />
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand" />
|
||||||
<icon-button
|
<icon-button
|
||||||
:icon="Unlink"
|
:icon="Unlink"
|
||||||
size="20"
|
size="20"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { NIcon, NSpace, NTag } from 'naive-ui'
|
||||||
import Key from '@/components/icons/Key.vue'
|
import Key from '@/components/icons/Key.vue'
|
||||||
import Binary from '@/components/icons/Binary.vue'
|
import Binary from '@/components/icons/Binary.vue'
|
||||||
import ToggleDb from '@/components/icons/ToggleDb.vue'
|
import ToggleDb from '@/components/icons/ToggleDb.vue'
|
||||||
import { find, get, includes, indexOf, isEmpty, remove, size } from 'lodash'
|
import { find, get, includes, indexOf, isEmpty, remove, size, startsWith } from 'lodash'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import Refresh from '@/components/icons/Refresh.vue'
|
import Refresh from '@/components/icons/Refresh.vue'
|
||||||
import CopyLink from '@/components/icons/CopyLink.vue'
|
import CopyLink from '@/components/icons/CopyLink.vue'
|
||||||
|
@ -28,6 +28,7 @@ import LoadAll from '@/components/icons/LoadAll.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
keyView: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
|
@ -53,14 +54,6 @@ const selectedKeys = computed(() => {
|
||||||
const data = computed(() => {
|
const data = computed(() => {
|
||||||
const dbs = get(connectionStore.databases, props.server, [])
|
const dbs = get(connectionStore.databases, props.server, [])
|
||||||
return dbs
|
return dbs
|
||||||
// return [
|
|
||||||
// {
|
|
||||||
// key: `${props.server}`,
|
|
||||||
// label: props.server,
|
|
||||||
// type: ConnectionType.Server,
|
|
||||||
// children: dbs,
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const backgroundColor = computed(() => {
|
const backgroundColor = computed(() => {
|
||||||
|
@ -218,6 +211,17 @@ const expandKey = (key) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resetExpandKey = (server, db, includeDB) => {
|
||||||
|
const prefix = `${server}/db${db}`
|
||||||
|
remove(expandedKeys.value, (k) => {
|
||||||
|
if (!!!includeDB) {
|
||||||
|
return k !== prefix && startsWith(k, prefix)
|
||||||
|
} else {
|
||||||
|
return startsWith(k, prefix)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleSelectContextMenu = (key) => {
|
const handleSelectContextMenu = (key) => {
|
||||||
contextMenuParam.show = false
|
contextMenuParam.show = false
|
||||||
const selectedKey = get(selectedKeys.value, 0)
|
const selectedKey = get(selectedKeys.value, 0)
|
||||||
|
@ -247,10 +251,11 @@ const handleSelectContextMenu = (key) => {
|
||||||
nextTick().then(() => expandKey(nodeKey))
|
nextTick().then(() => expandKey(nodeKey))
|
||||||
break
|
break
|
||||||
case 'db_reload':
|
case 'db_reload':
|
||||||
|
resetExpandKey(props.server, db)
|
||||||
connectionStore.reopenDatabase(props.server, db)
|
connectionStore.reopenDatabase(props.server, db)
|
||||||
break
|
break
|
||||||
case 'db_close':
|
case 'db_close':
|
||||||
remove(expandedKeys.value, (k) => k === `${props.server}/db${db}`)
|
resetExpandKey(props.server, db, true)
|
||||||
connectionStore.closeDatabase(props.server, db)
|
connectionStore.closeDatabase(props.server, db)
|
||||||
break
|
break
|
||||||
case 'db_newkey':
|
case 'db_newkey':
|
||||||
|
@ -327,6 +332,7 @@ const handleSelectContextMenu = (key) => {
|
||||||
console.warn('TODO: handle context menu:' + key)
|
console.warn('TODO: handle context menu:' + key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
handleSelectContextMenu,
|
handleSelectContextMenu,
|
||||||
})
|
})
|
||||||
|
@ -490,7 +496,7 @@ const renderIconMenu = (items) => {
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
inline: true,
|
inline: true,
|
||||||
size: 2,
|
size: 3,
|
||||||
wrapItem: false,
|
wrapItem: false,
|
||||||
wrap: false,
|
wrap: false,
|
||||||
style: 'margin-right: 5px',
|
style: 'margin-right: 5px',
|
||||||
|
@ -684,7 +690,6 @@ const handleOutsideContextMenu = () => {
|
||||||
@update:selected-keys="onUpdateSelectedKeys"
|
@update:selected-keys="onUpdateSelectedKeys"
|
||||||
@update:expanded-keys="onUpdateExpanded" />
|
@update:expanded-keys="onUpdateExpanded" />
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
:animated="false"
|
|
||||||
:options="contextMenuParam.options"
|
:options="contextMenuParam.options"
|
||||||
:render-label="renderContextLabel"
|
:render-label="renderContextLabel"
|
||||||
:show="contextMenuParam.show"
|
:show="contextMenuParam.show"
|
||||||
|
|
|
@ -166,7 +166,7 @@ const renderIconMenu = (items) => {
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
inline: true,
|
inline: true,
|
||||||
size: 2,
|
size: 3,
|
||||||
wrapItem: false,
|
wrapItem: false,
|
||||||
wrap: false,
|
wrap: false,
|
||||||
style: 'margin-right: 5px',
|
style: 'margin-right: 5px',
|
||||||
|
@ -530,10 +530,10 @@ const onCancelOpen = () => {
|
||||||
|
|
||||||
<!-- context menu -->
|
<!-- context menu -->
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
:animated="false"
|
|
||||||
:options="contextMenuParam.options"
|
:options="contextMenuParam.options"
|
||||||
:render-label="renderContextLabel"
|
:render-label="renderContextLabel"
|
||||||
:show="contextMenuParam.show"
|
:show="contextMenuParam.show"
|
||||||
|
:keyboard="true"
|
||||||
:x="contextMenuParam.x"
|
:x="contextMenuParam.x"
|
||||||
:y="contextMenuParam.y"
|
:y="contextMenuParam.y"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
|
|
|
@ -125,8 +125,6 @@ const openGithub = () => {
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<div class="nav-menu-item flex-box-v">
|
<div class="nav-menu-item flex-box-v">
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
:animated="false"
|
|
||||||
:keyboard="false"
|
|
||||||
:options="preferencesOptions"
|
:options="preferencesOptions"
|
||||||
:render-label="renderContextLabel"
|
:render-label="renderContextLabel"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* all types of redis key viewing
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
export const KeyViewType = {
|
||||||
|
Tree: 0,
|
||||||
|
List: 1,
|
||||||
|
}
|
|
@ -85,7 +85,7 @@
|
||||||
"remove_key": "Remove Key",
|
"remove_key": "Remove Key",
|
||||||
"new_key": "Add Key",
|
"new_key": "Add Key",
|
||||||
"load_more": "Load More Keys",
|
"load_more": "Load More Keys",
|
||||||
"load_all": "Load All 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. Please retry",
|
||||||
"empty_server_content": "Select and open a connection from the left",
|
"empty_server_content": "Select and open a connection from the left",
|
||||||
|
@ -149,6 +149,9 @@
|
||||||
"dbfilter_hide_title": "Select the Databases to Hide",
|
"dbfilter_hide_title": "Select the Databases to Hide",
|
||||||
"dbfilter_input": "Input Database Index",
|
"dbfilter_input": "Input Database Index",
|
||||||
"dbfilter_input_tip": "Press Enter to confirm",
|
"dbfilter_input_tip": "Press Enter to confirm",
|
||||||
|
"key_view": "Default Key View",
|
||||||
|
"key_view_tree": "Tree View",
|
||||||
|
"key_view_list": "List View",
|
||||||
"load_size": "Size of Keys Per Load",
|
"load_size": "Size of Keys Per Load",
|
||||||
"mark_color": "Mark Color"
|
"mark_color": "Mark Color"
|
||||||
},
|
},
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
"remove_key": "删除键",
|
"remove_key": "删除键",
|
||||||
"new_key": "添加新键",
|
"new_key": "添加新键",
|
||||||
"load_more": "加载更多键",
|
"load_more": "加载更多键",
|
||||||
"load_all": "加载所有键",
|
"load_all": "加载剩余所有键",
|
||||||
"more_action": "更多操作",
|
"more_action": "更多操作",
|
||||||
"nonexist_tab_content": "所选键不存在,请尝试刷新重试",
|
"nonexist_tab_content": "所选键不存在,请尝试刷新重试",
|
||||||
"empty_server_content": "可以从左边选择并打开连接",
|
"empty_server_content": "可以从左边选择并打开连接",
|
||||||
|
@ -149,6 +149,9 @@
|
||||||
"dbfilter_hide_title": "需要隐藏的数据库",
|
"dbfilter_hide_title": "需要隐藏的数据库",
|
||||||
"dbfilter_input": "输入数据库索引",
|
"dbfilter_input": "输入数据库索引",
|
||||||
"dbfilter_input_tip": "按回车确认",
|
"dbfilter_input_tip": "按回车确认",
|
||||||
|
"key_view": "默认键视图",
|
||||||
|
"key_view_tree": "树形列表",
|
||||||
|
"key_view_list": "平铺列表",
|
||||||
"load_size": "单次加载键数量",
|
"load_size": "单次加载键数量",
|
||||||
"mark_color": "标记颜色"
|
"mark_color": "标记颜色"
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,6 +51,8 @@ import { ConnectionType } from '@/consts/connection_type.js'
|
||||||
import useTabStore from './tab.js'
|
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 { nextTick } from 'vue'
|
||||||
|
|
||||||
const useConnectionStore = defineStore('connections', {
|
const useConnectionStore = defineStore('connections', {
|
||||||
/**
|
/**
|
||||||
|
@ -117,10 +119,12 @@ const useConnectionStore = defineStore('connections', {
|
||||||
connections: [], // all connections
|
connections: [], // all connections
|
||||||
serverStats: {}, // current server status info
|
serverStats: {}, // current server status info
|
||||||
serverProfile: {}, // all server profile
|
serverProfile: {}, // all server profile
|
||||||
keyFilter: {}, // all key filters in opened connections group by server+db
|
keyFilter: {}, // all key filters in opened connections group by 'server+db'
|
||||||
typeFilter: {}, // all key type filters in opened connections group by server+db
|
typeFilter: {}, // all key type filters in opened connections group by 'server+db'
|
||||||
databases: {}, // all databases in opened connections group by server name
|
viewType: {}, // view type selection for all opened connections group by 'server'
|
||||||
nodeMap: {}, // all nodes in opened connections group by server#db and type/key
|
databases: {}, // all databases 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
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
anyConnectionOpened() {
|
anyConnectionOpened() {
|
||||||
|
@ -232,6 +236,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
execTimeout: 60,
|
execTimeout: 60,
|
||||||
dbFilterType: 'none',
|
dbFilterType: 'none',
|
||||||
dbFilterList: [],
|
dbFilterList: [],
|
||||||
|
keyView: KeyViewType.Tree,
|
||||||
loadSize: 10000,
|
loadSize: 10000,
|
||||||
markColor: '',
|
markColor: '',
|
||||||
ssl: {
|
ssl: {
|
||||||
|
@ -320,6 +325,38 @@ const useConnectionStore = defineStore('connections', {
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* switch key view
|
||||||
|
* @param {string} connName
|
||||||
|
* @param {number} viewType
|
||||||
|
*/
|
||||||
|
async switchKeyView(connName, viewType) {
|
||||||
|
if (viewType !== KeyViewType.Tree && viewType !== KeyViewType.List) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = get(this.viewType, connName, KeyViewType.Tree)
|
||||||
|
if (t === viewType) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.viewType[connName] = viewType
|
||||||
|
const dbs = get(this.databases, connName, [])
|
||||||
|
for (const dbItem of dbs) {
|
||||||
|
if (!dbItem.opened) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dbItem.children = undefined
|
||||||
|
dbItem.keys = 0
|
||||||
|
const { db = 0 } = dbItem
|
||||||
|
this._getNodeMap(connName, db).clear()
|
||||||
|
const keys = this._getKeySet(connName, db)
|
||||||
|
this._addKeyNodes(connName, db, keys)
|
||||||
|
this._tidyNode(connName, db, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a new connection or update current connection profile
|
* create a new connection or update current connection profile
|
||||||
* @param {string} name set null if create a new connection
|
* @param {string} name set null if create a new connection
|
||||||
|
@ -399,13 +436,14 @@ const useConnectionStore = defineStore('connections', {
|
||||||
// if (connNode == null) {
|
// if (connNode == null) {
|
||||||
// throw new Error('no such connection')
|
// throw new Error('no such connection')
|
||||||
// }
|
// }
|
||||||
const { db } = data
|
const { db, view = KeyViewType.Tree } = data
|
||||||
if (isEmpty(db)) {
|
if (isEmpty(db)) {
|
||||||
throw new Error('no db loaded')
|
throw new Error('no db loaded')
|
||||||
}
|
}
|
||||||
const dbs = []
|
const dbs = []
|
||||||
for (let i = 0; i < db.length; i++) {
|
for (let i = 0; i < db.length; i++) {
|
||||||
this._getNodeMap(name, i).clear()
|
this._getNodeMap(name, i).clear()
|
||||||
|
this._getKeySet(name, i).clear()
|
||||||
dbs.push({
|
dbs.push({
|
||||||
key: `${name}/${db[i].name}`,
|
key: `${name}/${db[i].name}`,
|
||||||
label: db[i].name,
|
label: db[i].name,
|
||||||
|
@ -419,6 +457,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.databases[name] = dbs
|
this.databases[name] = dbs
|
||||||
|
this.viewType[name] = view
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -438,6 +477,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
for (const db of dbs) {
|
for (const db of dbs) {
|
||||||
this.removeKeyFilter(name, db.db)
|
this.removeKeyFilter(name, db.db)
|
||||||
this._getNodeMap(name, db.db).clear()
|
this._getNodeMap(name, db.db).clear()
|
||||||
|
this._getKeySet(name, db.db).clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.removeKeyFilter(name, -1)
|
this.removeKeyFilter(name, -1)
|
||||||
|
@ -459,6 +499,8 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.databases = {}
|
this.databases = {}
|
||||||
|
this.nodeMap.clear()
|
||||||
|
this.keySet.clear()
|
||||||
this.serverStats = {}
|
this.serverStats = {}
|
||||||
const tabStore = useTabStore()
|
const tabStore = useTabStore()
|
||||||
tabStore.removeAllTab()
|
tabStore.removeAllTab()
|
||||||
|
@ -534,8 +576,8 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async openDatabase(connName, db) {
|
async openDatabase(connName, db) {
|
||||||
const { match: filterPattern, type: keyType } = this.getKeyFilter(connName, db)
|
const { match: filterPattern, type: filterType } = this.getKeyFilter(connName, db)
|
||||||
const { data, success, msg } = await OpenDatabase(connName, db, filterPattern, keyType)
|
const { data, success, msg } = await OpenDatabase(connName, db, filterPattern, filterType)
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(msg)
|
throw new Error(msg)
|
||||||
}
|
}
|
||||||
|
@ -571,6 +613,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
selDB.isLeaf = false
|
selDB.isLeaf = false
|
||||||
|
|
||||||
this._getNodeMap(connName, db).clear()
|
this._getNodeMap(connName, db).clear()
|
||||||
|
this._getKeySet(connName, db).clear()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -588,6 +631,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
selDB.opened = false
|
selDB.opened = false
|
||||||
|
|
||||||
this._getNodeMap(connName, db).clear()
|
this._getNodeMap(connName, db).clear()
|
||||||
|
this._getKeySet(connName, db).clear()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -730,8 +774,6 @@ const useConnectionStore = defineStore('connections', {
|
||||||
async loadAllKeys(connName, db) {
|
async loadAllKeys(connName, db) {
|
||||||
const { match, type: keyType } = this.getKeyFilter(connName, db)
|
const { match, type: keyType } = this.getKeyFilter(connName, db)
|
||||||
const { keys } = await this._loadKeys(connName, db, match, keyType, true)
|
const { keys } = await this._loadKeys(connName, db, match, keyType, true)
|
||||||
// remove current keys below prefix
|
|
||||||
this._deleteKeyNode(connName, db, '', true)
|
|
||||||
this._addKeyNodes(connName, db, keys)
|
this._addKeyNodes(connName, db, keys)
|
||||||
this._tidyNode(connName, db, '')
|
this._tidyNode(connName, db, '')
|
||||||
},
|
},
|
||||||
|
@ -752,30 +794,50 @@ const useConnectionStore = defineStore('connections', {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get node map
|
* get node map
|
||||||
* @param connName
|
* @param {string} connName
|
||||||
* @param db
|
* @param {number} db
|
||||||
* @returns {Map<string, DatabaseItem>}
|
* @returns {Map<string, DatabaseItem>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getNodeMap(connName, db) {
|
_getNodeMap(connName, db) {
|
||||||
if (this.nodeMap[`${connName}#${db}`] == null) {
|
if (!this.nodeMap.hasOwnProperty(`${connName}#${db}`)) {
|
||||||
this.nodeMap[`${connName}#${db}`] = new Map()
|
this.nodeMap[`${connName}#${db}`] = new Map()
|
||||||
}
|
}
|
||||||
// construct a tree node list, the format of item key likes 'server/db#type/key'
|
// construct a tree node list, the format of item key likes 'server/db#type/key'
|
||||||
return this.nodeMap[`${connName}#${db}`]
|
return this.nodeMap[`${connName}#${db}`]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all keys in a database
|
||||||
|
* @param {string} connName
|
||||||
|
* @param {number} db
|
||||||
|
* @return {Set<string|number[]>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getKeySet(connName, db) {
|
||||||
|
if (!this.keySet.hasOwnProperty(`${connName}#${db}`)) {
|
||||||
|
this.keySet[`${connName}#${db}`] = new Set()
|
||||||
|
}
|
||||||
|
// construct a key set
|
||||||
|
return this.keySet[`${connName}#${db}`]
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove keys in db
|
* remove keys in db
|
||||||
* @param {string} connName
|
* @param {string} connName
|
||||||
* @param {number} db
|
* @param {number} db
|
||||||
* @param {Array<string|number[]>} keys
|
* @param {Array<string|number[]>|Set<string|number[]>} keys
|
||||||
* @param {boolean} [sortInsert]
|
* @param {boolean} [sortInsert]
|
||||||
* @return {{success: boolean, newKey: number, newLayer: number, replaceKey: number}}
|
* @return {{success: boolean, newKey: number, newLayer: number, replaceKey: number}}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_addKeyNodes(connName, db, keys, sortInsert) {
|
_addKeyNodes(connName, db, keys, sortInsert) {
|
||||||
const result = { success: false, newLayer: 0, newKey: 0, replaceKey: 0 }
|
const result = {
|
||||||
|
success: false,
|
||||||
|
newLayer: 0,
|
||||||
|
newKey: 0,
|
||||||
|
replaceKey: 0,
|
||||||
|
}
|
||||||
if (isEmpty(keys)) {
|
if (isEmpty(keys)) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -789,11 +851,46 @@ const useConnectionStore = defineStore('connections', {
|
||||||
selDB.children = []
|
selDB.children = []
|
||||||
}
|
}
|
||||||
const nodeMap = this._getNodeMap(connName, db)
|
const nodeMap = this._getNodeMap(connName, db)
|
||||||
|
const keySet = this._getKeySet(connName, db)
|
||||||
const rootChildren = selDB.children
|
const rootChildren = selDB.children
|
||||||
|
const viewType = get(this.viewType, connName, KeyViewType.Tree)
|
||||||
|
if (viewType === KeyViewType.List) {
|
||||||
|
// construct list view data
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const k = decodeRedisKey(key)
|
const k = decodeRedisKey(key)
|
||||||
const binaryKey = k !== key
|
const isBinaryKey = k !== key
|
||||||
const keyParts = binaryKey ? [nativeRedisKey(key)] : split(k, separator)
|
const nodeKey = `${ConnectionType.RedisValue}/${nativeRedisKey(key)}`
|
||||||
|
const replaceKey = nodeMap.has(nodeKey)
|
||||||
|
const selectedNode = {
|
||||||
|
key: `${connName}/db${db}#${nodeKey}`,
|
||||||
|
label: k,
|
||||||
|
db,
|
||||||
|
keys: 0,
|
||||||
|
redisKey: k,
|
||||||
|
redisKeyCode: isBinaryKey ? key : undefined,
|
||||||
|
type: ConnectionType.RedisValue,
|
||||||
|
isLeaf: true,
|
||||||
|
}
|
||||||
|
nodeMap.set(nodeKey, selectedNode)
|
||||||
|
keySet.add(key)
|
||||||
|
if (!replaceKey) {
|
||||||
|
if (sortInsert) {
|
||||||
|
const index = sortedIndexBy(rootChildren, selectedNode, 'key')
|
||||||
|
rootChildren.splice(index, 0, selectedNode)
|
||||||
|
} else {
|
||||||
|
rootChildren.push(selectedNode)
|
||||||
|
}
|
||||||
|
result.newKey += 1
|
||||||
|
} else {
|
||||||
|
result.replaceKey += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// construct tree view data
|
||||||
|
for (const key of keys) {
|
||||||
|
const k = decodeRedisKey(key)
|
||||||
|
const isBinaryKey = k !== key
|
||||||
|
const keyParts = isBinaryKey ? [nativeRedisKey(key)] : split(k, separator)
|
||||||
const len = size(keyParts)
|
const len = size(keyParts)
|
||||||
const lastIdx = len - 1
|
const lastIdx = len - 1
|
||||||
let handlePath = ''
|
let handlePath = ''
|
||||||
|
@ -817,9 +914,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
nodeMap.set(nodeKey, selectedNode)
|
nodeMap.set(nodeKey, selectedNode)
|
||||||
if (sortInsert) {
|
if (sortInsert) {
|
||||||
const index = sortedIndexBy(children, selectedNode, (elem) => {
|
const index = sortedIndexBy(children, selectedNode, 'key')
|
||||||
return elem.key
|
|
||||||
})
|
|
||||||
children.splice(index, 0, selectedNode)
|
children.splice(index, 0, selectedNode)
|
||||||
} else {
|
} else {
|
||||||
children.push(selectedNode)
|
children.push(selectedNode)
|
||||||
|
@ -834,20 +929,19 @@ const useConnectionStore = defineStore('connections', {
|
||||||
const replaceKey = nodeMap.has(nodeKey)
|
const replaceKey = nodeMap.has(nodeKey)
|
||||||
const selectedNode = {
|
const selectedNode = {
|
||||||
key: `${connName}/db${db}#${nodeKey}`,
|
key: `${connName}/db${db}#${nodeKey}`,
|
||||||
label: binaryKey ? k : keyParts[i],
|
label: isBinaryKey ? k : keyParts[i],
|
||||||
db,
|
db,
|
||||||
keys: 0,
|
keys: 0,
|
||||||
redisKey: handlePath,
|
redisKey: handlePath,
|
||||||
redisKeyCode: binaryKey ? key : undefined,
|
redisKeyCode: isBinaryKey ? key : undefined,
|
||||||
type: ConnectionType.RedisValue,
|
type: ConnectionType.RedisValue,
|
||||||
isLeaf: true,
|
isLeaf: true,
|
||||||
}
|
}
|
||||||
nodeMap.set(nodeKey, selectedNode)
|
nodeMap.set(nodeKey, selectedNode)
|
||||||
|
keySet.add(key)
|
||||||
if (!replaceKey) {
|
if (!replaceKey) {
|
||||||
if (sortInsert) {
|
if (sortInsert) {
|
||||||
const index = sortedIndexBy(children, selectedNode, (elem) => {
|
const index = sortedIndexBy(children, selectedNode, 'key')
|
||||||
return elem.key > selectedNode.key
|
|
||||||
})
|
|
||||||
children.splice(index, 0, selectedNode)
|
children.splice(index, 0, selectedNode)
|
||||||
} else {
|
} else {
|
||||||
children.push(selectedNode)
|
children.push(selectedNode)
|
||||||
|
@ -859,6 +953,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1408,8 +1503,9 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeMap = this._getNodeMap(connName, db)
|
const nodeMap = this._getNodeMap(connName, db)
|
||||||
|
const keySet = this._getKeySet(connName, db)
|
||||||
if (isLayer === true) {
|
if (isLayer === true) {
|
||||||
this._deleteChildrenKeyNodes(nodeMap, key)
|
this._deleteChildrenKeyNodes(nodeMap, keySet, key)
|
||||||
}
|
}
|
||||||
if (isEmpty(key)) {
|
if (isEmpty(key)) {
|
||||||
// clear all key nodes
|
// clear all key nodes
|
||||||
|
@ -1447,12 +1543,18 @@ const useConnectionStore = defineStore('connections', {
|
||||||
|
|
||||||
if (isEmpty(anceNode.children)) {
|
if (isEmpty(anceNode.children)) {
|
||||||
nodeMap.delete(`${ConnectionType.RedisKey}/${anceKey}`)
|
nodeMap.delete(`${ConnectionType.RedisKey}/${anceKey}`)
|
||||||
|
keySet.delete(anceNode.redisKeyCode || anceNode.redisKey)
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// last one, remove from db node
|
// last one, remove from db node
|
||||||
remove(dbRoot.children, { type: ConnectionType.RedisKey, redisKey: keyParts[0] })
|
remove(dbRoot.children, { type: ConnectionType.RedisKey, redisKey: keyParts[0] })
|
||||||
|
const node = nodeMap.get(`${ConnectionType.RedisValue}/${keyParts[0]}`)
|
||||||
|
if (node != null) {
|
||||||
|
nodeMap.delete(`${ConnectionType.RedisValue}/${keyParts[0]}`)
|
||||||
|
keySet.delete(node.redisKeyCode || node.redisKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1463,12 +1565,14 @@ const useConnectionStore = defineStore('connections', {
|
||||||
/**
|
/**
|
||||||
* delete node and all it's children from nodeMap
|
* delete node and all it's children from nodeMap
|
||||||
* @param {Map<string, DatabaseItem>} nodeMap
|
* @param {Map<string, DatabaseItem>} nodeMap
|
||||||
|
* @param {Set<string|number[]>} keySet
|
||||||
* @param {string} [key] clean nodeMap if key is empty
|
* @param {string} [key] clean nodeMap if key is empty
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_deleteChildrenKeyNodes(nodeMap, key) {
|
_deleteChildrenKeyNodes(nodeMap, keySet, key) {
|
||||||
if (isEmpty(key)) {
|
if (isEmpty(key)) {
|
||||||
nodeMap.clear()
|
nodeMap.clear()
|
||||||
|
keySet.clear()
|
||||||
} else {
|
} else {
|
||||||
const mapKey = `${ConnectionType.RedisKey}/${key}`
|
const mapKey = `${ConnectionType.RedisKey}/${key}`
|
||||||
const node = nodeMap.get(mapKey)
|
const node = nodeMap.get(mapKey)
|
||||||
|
@ -1477,13 +1581,15 @@ const useConnectionStore = defineStore('connections', {
|
||||||
if (!nodeMap.delete(`${ConnectionType.RedisValue}/${child.redisKey}`)) {
|
if (!nodeMap.delete(`${ConnectionType.RedisValue}/${child.redisKey}`)) {
|
||||||
console.warn('delete:', `${ConnectionType.RedisValue}/${child.redisKey}`)
|
console.warn('delete:', `${ConnectionType.RedisValue}/${child.redisKey}`)
|
||||||
}
|
}
|
||||||
|
keySet.delete(child.redisKeyCode || child.redisKey)
|
||||||
} else if (child.type === ConnectionType.RedisKey) {
|
} else if (child.type === ConnectionType.RedisKey) {
|
||||||
this._deleteChildrenKeyNodes(nodeMap, child.redisKey)
|
this._deleteChildrenKeyNodes(nodeMap, keySet, child.redisKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!nodeMap.delete(mapKey)) {
|
if (!nodeMap.delete(mapKey)) {
|
||||||
console.warn('delete map key', mapKey)
|
console.warn('delete map key', mapKey)
|
||||||
}
|
}
|
||||||
|
keySet.delete(node.redisKeyCode || node.redisKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue