Compare commits

..

No commits in common. "b688ded610bcf2fd7cc2be46565c3f1c004ffd9f" and "7934fc275ac11e49a8099e9a936017c6b9a59965" have entirely different histories.

21 changed files with 182 additions and 523 deletions

View File

@ -291,7 +291,9 @@ func (b *browserService) getRedisClient(connName string, db int) (item connectio
if db >= 0 { if db >= 0 {
var rdb *redis.Client var rdb *redis.Client
if rdb, ok = client.(*redis.Client); ok && rdb != nil { 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
}
} }
} }
return return
@ -1770,8 +1772,8 @@ func (b *browserService) SetKeyTTL(connName string, db int, k any, ttl int64) (r
} }
// DeleteKey remove redis key // DeleteKey remove redis key
func (b *browserService) DeleteKey(server string, db int, k any, async bool) (resp types.JSResp) { func (b *browserService) DeleteKey(connName string, db int, k any, async bool) (resp types.JSResp) {
item, err := b.getRedisClient(server, db) item, err := b.getRedisClient(connName, db)
if err != nil { if err != nil {
resp.Msg = err.Error() resp.Msg = err.Error()
return return
@ -1860,34 +1862,6 @@ func (b *browserService) DeleteKey(server string, db int, k any, async bool) (re
return return
} }
// DeleteOneKey delete one key
func (b *browserService) DeleteOneKey(server string, db int, k any) (resp types.JSResp) {
item, err := b.getRedisClient(server, db)
if err != nil {
resp.Msg = err.Error()
return
}
client, ctx := item.client, item.ctx
key := strutil.DecodeRedisKey(k)
if cluster, ok := client.(*redis.ClusterClient); ok {
// cluster mode
err = cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
return cli.Del(ctx, key).Err()
})
} else {
err = client.Del(ctx, key).Err()
}
if err != nil {
resp.Msg = err.Error()
return
}
resp.Success = true
return
}
// FlushDB flush database // FlushDB flush database
func (b *browserService) FlushDB(connName string, db int, async bool) (resp types.JSResp) { func (b *browserService) FlushDB(connName string, db int, async bool) (resp types.JSResp) {
item, err := b.getRedisClient(connName, db) item, err := b.getRedisClient(connName, db)

View File

@ -24,7 +24,6 @@ func NewPreferences() Preferences {
}, },
Editor: PreferencesEditor{ Editor: PreferencesEditor{
FontSize: consts.DEFAULT_FONT_SIZE, FontSize: consts.DEFAULT_FONT_SIZE,
ShowLineNum: true,
}, },
} }
} }
@ -51,5 +50,4 @@ type PreferencesGeneral struct {
type PreferencesEditor struct { type PreferencesEditor struct {
Font string `json:"font" yaml:"font,omitempty"` Font string `json:"font" yaml:"font,omitempty"`
FontSize int `json:"fontSize" yaml:"font_size"` FontSize int `json:"fontSize" yaml:"font_size"`
ShowLineNum bool `json:"showLineNum" yaml:"show_line_num,omitempty"`
} }

View File

@ -4,6 +4,9 @@ import NewKeyDialog from './components/dialogs/NewKeyDialog.vue'
import PreferencesDialog from './components/dialogs/PreferencesDialog.vue' import PreferencesDialog from './components/dialogs/PreferencesDialog.vue'
import RenameKeyDialog from './components/dialogs/RenameKeyDialog.vue' import RenameKeyDialog from './components/dialogs/RenameKeyDialog.vue'
import SetTtlDialog from './components/dialogs/SetTtlDialog.vue' import SetTtlDialog from './components/dialogs/SetTtlDialog.vue'
import hljs from 'highlight.js/lib/core'
import json from 'highlight.js/lib/languages/json'
import plaintext from 'highlight.js/lib/languages/plaintext'
import AddFieldsDialog from './components/dialogs/AddFieldsDialog.vue' import AddFieldsDialog from './components/dialogs/AddFieldsDialog.vue'
import AppContent from './AppContent.vue' import AppContent from './AppContent.vue'
import GroupDialog from './components/dialogs/GroupDialog.vue' import GroupDialog from './components/dialogs/GroupDialog.vue'
@ -19,6 +22,9 @@ import { darkThemeOverrides, themeOverrides } from '@/utils/theme.js'
import AboutDialog from '@/components/dialogs/AboutDialog.vue' import AboutDialog from '@/components/dialogs/AboutDialog.vue'
import FlushDbDialog from '@/components/dialogs/FlushDbDialog.vue' import FlushDbDialog from '@/components/dialogs/FlushDbDialog.vue'
hljs.registerLanguage('json', json)
hljs.registerLanguage('plaintext', plaintext)
const prefStore = usePreferencesStore() const prefStore = usePreferencesStore()
const connectionStore = useConnectionStore() const connectionStore = useConnectionStore()
const i18n = useI18n() const i18n = useI18n()
@ -51,6 +57,7 @@ watch(
<template> <template>
<n-config-provider <n-config-provider
:hljs="hljs"
:inline-theme-disabled="true" :inline-theme-disabled="true"
:locale="prefStore.themeLocale" :locale="prefStore.themeLocale"
:theme="prefStore.isDark ? darkTheme : undefined" :theme="prefStore.isDark ? darkTheme : undefined"

View File

@ -12,7 +12,6 @@ import WindowClose from '@/components/icons/WindowClose.vue'
import Pin from '@/components/icons/Pin.vue' import Pin from '@/components/icons/Pin.vue'
import OffScreen from '@/components/icons/OffScreen.vue' import OffScreen from '@/components/icons/OffScreen.vue'
import ContentEditor from '@/components/content_value/ContentEditor.vue' import ContentEditor from '@/components/content_value/ContentEditor.vue'
import usePreferencesStore from 'stores/preferences.js'
const props = defineProps({ const props = defineProps({
field: { field: {
@ -43,7 +42,6 @@ const props = defineProps({
const themeVars = useThemeVars() const themeVars = useThemeVars()
const browserStore = useBrowserStore() const browserStore = useBrowserStore()
const prefStore = usePreferencesStore()
const emit = defineEmits([ const emit = defineEmits([
'update:field', 'update:field',
'update:value', 'update:value',
@ -171,7 +169,6 @@ const onSave = () => {
:border="true" :border="true"
:content="displayValue" :content="displayValue"
:language="viewLanguage" :language="viewLanguage"
:show-line-num="prefStore.showLineNum"
class="flex-item-expand" class="flex-item-expand"
style="height: 100%" style="height: 100%"
@input="onInput" @input="onInput"

View File

@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue' import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
import EditableTableColumn from '@/components/common/EditableTableColumn.vue' import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
import useDialogStore from 'stores/dialog.js' import useDialogStore from 'stores/dialog.js'
@ -126,7 +126,7 @@ const valueColumn = computed(() => ({
// }, // },
render: (row) => { render: (row) => {
if (displayCode.value) { if (displayCode.value) {
return h('pre', {}, row.dv || row.v) return h(NCode, { language: 'json', wordWrap: true, code: row.dv || row.v })
} }
return row.dv || row.v return row.dv || row.v
}, },
@ -280,11 +280,6 @@ const entries = computed(() => {
return `${len} / ${Math.max(len, props.length)}` return `${len} / ${Math.max(len, props.length)}`
}) })
const loadProgress = computed(() => {
const len = size(props.value)
return (len * 100) / Math.max(len, props.length)
})
const onAddRow = () => { const onAddRow = () => {
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.HASH) dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.HASH)
} }
@ -360,16 +355,6 @@ defineExpose({
{{ $t('interface.add_row') }} {{ $t('interface.add_row') }}
</n-button> </n-button>
</div> </div>
<!-- loaded progress -->
<n-progress
:border-radius="0"
:color="props.end ? '#0000' : themeVars.primaryColor"
:height="2"
:percentage="loadProgress"
:processing="props.loading"
:show-indicator="false"
status="success"
type="line" />
<div id="content-table" class="value-wrapper value-item-part flex-box-h flex-item-expand"> <div id="content-table" class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table --> <!-- table -->
<n-data-table <n-data-table

View File

@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue' import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
import { isEmpty, size } from 'lodash' import { isEmpty, size } from 'lodash'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
import EditableTableColumn from '@/components/common/EditableTableColumn.vue' import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
@ -102,7 +102,7 @@ const valueColumn = computed(() => ({
}, },
render: (row) => { render: (row) => {
if (displayCode.value) { if (displayCode.value) {
return h('pre', {}, row.dv || row.v) return h(NCode, { language: 'json', wordWrap: true, code: row.dv || row.v })
} }
return row.dv || row.v return row.dv || row.v
}, },
@ -257,11 +257,6 @@ const entries = computed(() => {
return `${len} / ${Math.max(len, props.length)}` return `${len} / ${Math.max(len, props.length)}`
}) })
const loadProgress = computed(() => {
const len = size(props.value)
return (len * 100) / Math.max(len, props.length)
})
const onAddValue = (value) => { const onAddValue = (value) => {
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.LIST) dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.LIST)
} }
@ -337,16 +332,6 @@ defineExpose({
{{ $t('interface.add_row') }} {{ $t('interface.add_row') }}
</n-button> </n-button>
</div> </div>
<!-- loaded progress -->
<n-progress
:border-radius="0"
:color="props.end ? '#0000' : themeVars.primaryColor"
:height="2"
:percentage="loadProgress"
:processing="props.loading"
:show-indicator="false"
status="success"
type="line" />
<div class="value-wrapper value-item-part flex-box-h flex-item-expand"> <div class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table --> <!-- table -->
<n-data-table <n-data-table

View File

@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue' import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
import { isEmpty, size } from 'lodash' import { isEmpty, size } from 'lodash'
import useDialogStore from 'stores/dialog.js' import useDialogStore from 'stores/dialog.js'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
@ -101,7 +101,7 @@ const valueColumn = computed(() => ({
}, },
render: (row) => { render: (row) => {
if (displayCode.value) { if (displayCode.value) {
return h('pre', {}, row.dv || row.v) return h(NCode, { language: 'json', wordWrap: true, code: row.dv || row.v })
} }
return row.dv || row.v return row.dv || row.v
}, },
@ -254,11 +254,6 @@ const entries = computed(() => {
return `${len} / ${Math.max(len, props.length)}` return `${len} / ${Math.max(len, props.length)}`
}) })
const loadProgress = computed(() => {
const len = size(props.value)
return (len * 100) / Math.max(len, props.length)
})
const onAddValue = (value) => { const onAddValue = (value) => {
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.SET) dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.SET)
} }
@ -334,16 +329,6 @@ defineExpose({
{{ $t('interface.add_row') }} {{ $t('interface.add_row') }}
</n-button> </n-button>
</div> </div>
<!-- loaded progress -->
<n-progress
:border-radius="0"
:color="props.end ? '#0000' : themeVars.primaryColor"
:height="2"
:percentage="loadProgress"
:processing="props.loading"
:show-indicator="false"
status="success"
type="line" />
<div class="value-wrapper value-item-part flex-box-h flex-item-expand"> <div class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table --> <!-- table -->
<n-data-table <n-data-table

View File

@ -3,7 +3,7 @@ import { computed, h, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue' import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
import EditableTableColumn from '@/components/common/EditableTableColumn.vue' import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
import useDialogStore from 'stores/dialog.js' import useDialogStore from 'stores/dialog.js'
@ -94,7 +94,7 @@ const valueColumn = computed(() => ({
}, },
// sorter: (row1, row2) => row1.value - row2.value, // sorter: (row1, row2) => row1.value - row2.value,
render: (row) => { render: (row) => {
return h('pre', {}, row.dv) return h(NCode, { language: 'json', wordWrap: true, code: row.dv })
}, },
})) }))
const actionColumn = { const actionColumn = {
@ -145,11 +145,6 @@ const entries = computed(() => {
return `${len} / ${Math.max(len, props.length)}` return `${len} / ${Math.max(len, props.length)}`
}) })
const loadProgress = computed(() => {
const len = size(props.value)
return (len * 100) / Math.max(len, props.length)
})
const onAddRow = () => { const onAddRow = () => {
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.STREAM) dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.STREAM)
} }
@ -220,16 +215,6 @@ defineExpose({
{{ $t('interface.add_row') }} {{ $t('interface.add_row') }}
</n-button> </n-button>
</div> </div>
<!-- loaded progress -->
<n-progress
:border-radius="0"
:color="props.end ? '#0000' : themeVars.primaryColor"
:height="2"
:percentage="loadProgress"
:processing="props.loading"
:show-indicator="false"
status="success"
type="line" />
<div class="value-wrapper value-item-part flex-box-v flex-item-expand"> <div class="value-wrapper value-item-part flex-box-v flex-item-expand">
<n-data-table <n-data-table
:bordered="false" :bordered="false"

View File

@ -1,11 +1,11 @@
<script setup> <script setup>
import { computed, reactive, ref, watchEffect } from 'vue' import { computed, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue' import ContentToolbar from './ContentToolbar.vue'
import Copy from '@/components/icons/Copy.vue' import Copy from '@/components/icons/Copy.vue'
import Save from '@/components/icons/Save.vue' import Save from '@/components/icons/Save.vue'
import { useThemeVars } from 'naive-ui' import { useThemeVars } from 'naive-ui'
import { formatTypes } from '@/consts/value_view_type.js' import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import { types as redisTypes } from '@/consts/support_redis_type.js' import { types as redisTypes } from '@/consts/support_redis_type.js'
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js' import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
import { isEmpty, toLower } from 'lodash' import { isEmpty, toLower } from 'lodash'
@ -14,7 +14,9 @@ import useBrowserStore from 'stores/browser.js'
import { decodeRedisKey } from '@/utils/key_convert.js' import { decodeRedisKey } from '@/utils/key_convert.js'
import FormatSelector from '@/components/content_value/FormatSelector.vue' import FormatSelector from '@/components/content_value/FormatSelector.vue'
import ContentEditor from '@/components/content_value/ContentEditor.vue' import ContentEditor from '@/components/content_value/ContentEditor.vue'
import usePreferencesStore from 'stores/preferences.js'
const i18n = useI18n()
const themeVars = useThemeVars()
const props = defineProps({ const props = defineProps({
name: String, name: String,
@ -36,10 +38,6 @@ const props = defineProps({
const emit = defineEmits(['reload', 'rename', 'delete']) const emit = defineEmits(['reload', 'rename', 'delete'])
const i18n = useI18n()
const themeVars = useThemeVars()
const prefStore = usePreferencesStore()
/** /**
* *
* @type {ComputedRef<string|number[]>} * @type {ComputedRef<string|number[]>}
@ -60,8 +58,8 @@ const viewLanguage = computed(() => {
const viewAs = reactive({ const viewAs = reactive({
value: '', value: '',
format: '', format: formatTypes.RAW,
decode: '', decode: decodeTypes.NONE,
}) })
const editingContent = ref('') const editingContent = ref('')
@ -76,13 +74,13 @@ const displayValue = computed(() => {
return viewAs.value || decodeRedisKey(props.value) return viewAs.value || decodeRedisKey(props.value)
}) })
watchEffect( watch(
() => { () => props.value,
if (props.value !== undefined) { (val, oldVal) => {
if (val !== undefined) {
onFormatChanged(viewAs.decode, viewAs.format) onFormatChanged(viewAs.decode, viewAs.format)
} }
}, },
{ flush: 'post' },
) )
const converting = ref(false) const converting = ref(false)
@ -210,7 +208,6 @@ defineExpose({
:content="displayValue" :content="displayValue"
:language="viewLanguage" :language="viewLanguage"
:loading="props.loading" :loading="props.loading"
:show-line-num="prefStore.showLineNum"
class="flex-item-expand" class="flex-item-expand"
style="height: 100%" style="height: 100%"
@input="onInput" @input="onInput"

View File

@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue' import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
import EditableTableColumn from '@/components/common/EditableTableColumn.vue' import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
import { isEmpty, size } from 'lodash' import { isEmpty, size } from 'lodash'
@ -151,7 +151,7 @@ const valueColumn = computed(() => ({
// sorter: (row1, row2) => row1.value - row2.value, // sorter: (row1, row2) => row1.value - row2.value,
render: (row) => { render: (row) => {
if (displayCode.value) { if (displayCode.value) {
return h('pre', {}, row.dv || row.v) return h(NCode, { language: 'json', wordWrap: true, code: row.dv || row.v })
} }
return row.dv || row.v return row.dv || row.v
}, },
@ -291,11 +291,6 @@ const entries = computed(() => {
return `${len} / ${Math.max(len, props.length)}` return `${len} / ${Math.max(len, props.length)}`
}) })
const loadProgress = computed(() => {
const len = size(props.value)
return (len * 100) / Math.max(len, props.length)
})
const onAddRow = () => { const onAddRow = () => {
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.ZSET) dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.ZSET)
} }
@ -371,16 +366,6 @@ defineExpose({
{{ $t('interface.add_row') }} {{ $t('interface.add_row') }}
</n-button> </n-button>
</div> </div>
<!-- loaded progress -->
<n-progress
:border-radius="0"
:color="props.end ? '#0000' : themeVars.primaryColor"
:height="2"
:percentage="loadProgress"
:processing="props.loading"
:show-indicator="false"
status="success"
type="line" />
<div class="value-wrapper value-item-part flex-box-h flex-item-expand"> <div class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table --> <!-- table -->
<n-data-table <n-data-table

View File

@ -1,10 +1,9 @@
<script setup> <script setup>
import { computed, reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import useDialog from 'stores/dialog' import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { isEmpty, map, size } from 'lodash' import { isEmpty, size } from 'lodash'
import useBrowserStore from 'stores/browser.js' import useBrowserStore from 'stores/browser.js'
import { decodeRedisKey } from '@/utils/key_convert.js'
const deleteForm = reactive({ const deleteForm = reactive({
server: '', server: '',
@ -26,23 +25,16 @@ watch(
deleteForm.server = server deleteForm.server = server
deleteForm.db = db deleteForm.db = db
deleteForm.key = key deleteForm.key = key
deleteForm.loadingAffected = false
// deleteForm.async = true
loading.value = false
deleting.value = false
if (key instanceof Array) {
deleteForm.showAffected = true
deleteForm.affectedKeys = key
} else {
deleteForm.showAffected = false deleteForm.showAffected = false
deleteForm.loadingAffected = false
deleteForm.affectedKeys = [] deleteForm.affectedKeys = []
} deleteForm.async = true
loading.value = false
} }
}, },
) )
const loading = ref(false) const loading = ref(false)
const deleting = ref(false)
const scanAffectedKey = async () => { const scanAffectedKey = async () => {
try { try {
loading.value = true loading.value = true
@ -61,21 +53,20 @@ const resetAffected = () => {
deleteForm.affectedKeys = [] deleteForm.affectedKeys = []
} }
const logLines = computed(() => {
return map(deleteForm.affectedKeys, (k) => decodeRedisKey(k))
})
const i18n = useI18n() const i18n = useI18n()
const onConfirmDelete = async () => { const onConfirmDelete = async () => {
try { try {
deleting.value = true loading.value = true
const { server, db, key, affectedKeys } = deleteForm const { server, db, key, async } = deleteForm
browserStore.deleteKeys(server, db, affectedKeys).catch((e) => {}) const success = await browserStore.deleteKeyPrefix(server, db, key, async)
if (success) {
$message.success(i18n.t('dialogue.handle_succ'))
}
} catch (e) { } catch (e) {
$message.error(e.message) $message.error(e.message)
return return
} finally { } finally {
deleting.value = false loading.value = false
} }
dialogStore.closeDeleteKeyDialog() dialogStore.closeDeleteKeyDialog()
} }
@ -97,35 +88,29 @@ const onClose = () => {
transform-origin="center"> transform-origin="center">
<n-spin :show="loading"> <n-spin :show="loading">
<n-form :model="deleteForm" :show-require-mark="false" label-placement="top"> <n-form :model="deleteForm" :show-require-mark="false" label-placement="top">
<n-grid :x-gap="10"> <n-form-item :label="$t('dialogue.key.server')">
<n-form-item-gi :label="$t('dialogue.key.server')" :span="12">
<n-input :value="deleteForm.server" readonly /> <n-input :value="deleteForm.server" readonly />
</n-form-item-gi> </n-form-item>
<n-form-item-gi :label="$t('dialogue.key.db_index')" :span="12"> <n-form-item :label="$t('dialogue.key.db_index')">
<n-input :value="deleteForm.db.toString()" readonly /> <n-input :value="deleteForm.db.toString()" readonly />
</n-form-item-gi> </n-form-item>
</n-grid> <n-form-item :label="$t('dialogue.key.key_expression')" required>
<n-form-item
v-if="!(deleteForm.key instanceof Array)"
:label="$t('dialogue.key.key_expression')"
required>
<n-input v-model:value="deleteForm.key" placeholder="" @input="resetAffected" /> <n-input v-model:value="deleteForm.key" placeholder="" @input="resetAffected" />
</n-form-item> </n-form-item>
<!-- <n-form-item :label="$t('dialogue.key.async_delete')" required>--> <n-form-item :label="$t('dialogue.key.async_delete')" required>
<!-- <n-checkbox v-model:checked="deleteForm.async">--> <n-checkbox v-model:checked="deleteForm.async">
<!-- {{ $t('dialogue.key.async_delete_title') }}--> {{ $t('dialogue.key.async_delete_title') }}
<!-- </n-checkbox>--> </n-checkbox>
<!-- </n-form-item>--> </n-form-item>
<n-card <n-card
v-if="deleteForm.showAffected" v-if="deleteForm.showAffected"
:title="$t('dialogue.key.affected_key') + `(${size(deleteForm.affectedKeys)})`" :title="$t('dialogue.key.affected_key') + `(${size(deleteForm.affectedKeys)})`"
embedded
size="small"> size="small">
<n-skeleton v-if="deleteForm.loadingAffected" :repeat="10" text /> <n-skeleton v-if="deleteForm.loadingAffected" :repeat="10" text />
<n-log <n-log
v-else v-else
:line-height="1.5" :line-height="1.5"
:lines="logLines" :lines="deleteForm.affectedKeys"
:rows="10" :rows="10"
style="user-select: text; cursor: text" /> style="user-select: text; cursor: text" />
</n-card> </n-card>

View File

@ -123,9 +123,6 @@ const onClose = () => {
<n-form-item :label="$t('preferences.general.font_size')"> <n-form-item :label="$t('preferences.general.font_size')">
<n-input-number v-model:value="prefStore.editor.fontSize" :max="65535" :min="1" /> <n-input-number v-model:value="prefStore.editor.fontSize" :max="65535" :min="1" />
</n-form-item> </n-form-item>
<n-checkbox v-model:checked="prefStore.editor.showLineNum">
{{ $t('preferences.editor.show_linenum') }}
</n-checkbox>
</n-form> </n-form>
</n-tab-pane> </n-tab-pane>
</n-tabs> </n-tabs>

View File

@ -1,63 +0,0 @@
<script setup>
const props = defineProps({
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M20 24H44H20Z" fill="none" fill-rule="evenodd" />
<path
:stroke-width="props.strokeWidth"
d="M20 24H44"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path clip-rule="evenodd" d="M20 38H44H20Z" fill="none" fill-rule="evenodd" />
<path
:stroke-width="props.strokeWidth"
d="M20 38H44"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path clip-rule="evenodd" d="M20 10H44H20Z" fill="none" fill-rule="evenodd" />
<path
:stroke-width="props.strokeWidth"
d="M20 10H44"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linejoin="round"
width="8"
x="4"
y="34" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linejoin="round"
width="8"
x="4"
y="20" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linejoin="round"
width="8"
x="4"
y="6" />
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -4,7 +4,7 @@ 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'
import { computed, nextTick, onMounted, reactive, ref, unref, watch } from 'vue' import { computed, nextTick, onMounted, reactive, ref, unref, watch } from 'vue'
import { find, get, map } from 'lodash' import { get, map } from 'lodash'
import Refresh from '@/components/icons/Refresh.vue' import Refresh from '@/components/icons/Refresh.vue'
import useDialogStore from 'stores/dialog.js' import useDialogStore from 'stores/dialog.js'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -20,9 +20,6 @@ import RedisTypeSelector from '@/components/common/RedisTypeSelector.vue'
import { types } from '@/consts/support_redis_type.js' import { types } from '@/consts/support_redis_type.js'
import Plus from '@/components/icons/Plus.vue' import Plus from '@/components/icons/Plus.vue'
import useConnectionStore from 'stores/connections.js' import useConnectionStore from 'stores/connections.js'
import ListCheckbox from '@/components/icons/ListCheckbox.vue'
import Close from '@/components/icons/Close.vue'
import More from '@/components/icons/More.vue'
const themeVars = useThemeVars() const themeVars = useThemeVars()
const i18n = useI18n() const i18n = useI18n()
@ -36,8 +33,6 @@ const currentName = computed(() => get(tabStore.currentTab, 'name', ''))
const browserTreeRef = ref(null) const browserTreeRef = ref(null)
const loading = ref(false) const loading = ref(false)
const fullyLoaded = ref(false) const fullyLoaded = ref(false)
const inCheckState = ref(false)
const checkedCount = ref(0)
const selectedDB = computed(() => { const selectedDB = computed(() => {
return browserStore.selectedDatabases[currentName.value] || 0 return browserStore.selectedDatabases[currentName.value] || 0
@ -59,29 +54,12 @@ const dbSelectOptions = computed(() => {
}) })
}) })
const moreOptions = computed(() => {
return [
{ key: 'flush', label: i18n.t('interface.flush_db'), icon: render.renderIcon(Delete, { strokeWidth: 3.5 }) },
{
key: 'disconnect',
label: i18n.t('interface.disconnect'),
icon: render.renderIcon(Unlink, { strokeWidth: 3.5 }),
},
]
})
const loadProgress = computed(() => { const loadProgress = computed(() => {
const db = browserStore.getDatabase(currentName.value, selectedDB.value) const db = browserStore.getDatabase(currentName.value, selectedDB.value)
if (db.maxKeys <= 0) { if (db.maxKeys <= 0) {
return 100 return 100
} }
return (db.keys * 100) / Math.max(db.keys, db.maxKeys) return (db.keys * 100) / db.maxKeys
})
const checkedTip = computed(() => {
const dblist = browserStore.getDBList(currentName.value)
const db = find(dblist, { db: selectedDB.value })
return `${checkedCount.value} / ${db.maxKeys}`
}) })
const onReload = async () => { const onReload = async () => {
@ -137,10 +115,6 @@ const onLoadAll = async () => {
} }
} }
const onDeleteChecked = () => {
browserTreeRef.value?.deleteCheckedItems()
}
const onFlush = () => { const onFlush = () => {
dialogStore.openFlushDBDialog(currentName.value, selectedDB.value) dialogStore.openFlushDBDialog(currentName.value, selectedDB.value)
} }
@ -172,17 +146,6 @@ const onMatchInput = (matchVal, filterVal) => {
onReload() onReload()
} }
const onSelectOptions = (select) => {
switch (select) {
case 'flush':
onFlush()
break
case 'disconnect':
onDisconnect()
break
}
}
watch( watch(
() => browserStore.openedDB[currentName.value], () => browserStore.openedDB[currentName.value],
async (db, prevDB) => { async (db, prevDB) => {
@ -195,7 +158,7 @@ watch(
browserStore.closeDatabase(currentName.value, prevDB) browserStore.closeDatabase(currentName.value, prevDB)
browserStore.setKeyFilter(currentName.value, {}) browserStore.setKeyFilter(currentName.value, {})
await browserStore.openDatabase(currentName.value, db) await browserStore.openDatabase(currentName.value, db)
// browserTreeRef.value?.resetExpandKey(currentName.value, db) browserTreeRef.value?.resetExpandKey(currentName.value, db)
fullyLoaded.value = await browserStore.loadMoreKeys(currentName.value, db) fullyLoaded.value = await browserStore.loadMoreKeys(currentName.value, db)
browserTreeRef.value?.refreshTree() browserTreeRef.value?.refreshTree()
@ -269,9 +232,6 @@ onMounted(() => onReload())
<!-- tree view --> <!-- tree view -->
<browser-tree <browser-tree
ref="browserTreeRef" ref="browserTreeRef"
v-model:checked-count="checkedCount"
:check-mode="inCheckState"
:db="browserStore.openedDB[currentName]"
:full-loaded="fullyLoaded" :full-loaded="fullyLoaded"
:loading="loading && loadProgress <= 0" :loading="loading && loadProgress <= 0"
:pattern="filterForm.filter" :pattern="filterForm.filter"
@ -285,8 +245,7 @@ onMounted(() => onReload())
<!-- :stroke-width="3.5"--> <!-- :stroke-width="3.5"-->
<!-- unselect-stroke-width="3"--> <!-- unselect-stroke-width="3"-->
<!-- @update:value="onSwitchView" />--> <!-- @update:value="onSwitchView" />-->
<transition mode="out-in" name="fade"> <div class="flex-box-h nav-pane-func">
<div v-if="!inCheckState" class="flex-box-h nav-pane-func">
<n-select <n-select
v-model:value="browserStore.openedDB[currentName]" v-model:value="browserStore.openedDB[currentName]"
:consistent-menu-width="false" :consistent-menu-width="false"
@ -317,38 +276,19 @@ onMounted(() => onReload())
<div class="flex-item-expand" style="min-width: 10px" /> <div class="flex-item-expand" style="min-width: 10px" />
<icon-button <icon-button
:button-class="['nav-pane-func-btn']" :button-class="['nav-pane-func-btn']"
:icon="ListCheckbox"
:stroke-width="3.5"
size="20"
t-tooltip="interface.check_mode"
@click="inCheckState = true" />
<n-dropdown :options="moreOptions" placement="top-end" @select="onSelectOptions">
<icon-button :button-class="['nav-pane-func-btn']" :icon="More" :stroke-width="3.5" size="20" />
</n-dropdown>
</div>
<!-- check mode function bar -->
<div v-else class="flex-box-h nav-pane-func">
<icon-button
:button-class="['nav-pane-func-btn']"
:disabled="checkedCount <= 0"
:icon="Delete" :icon="Delete"
:stroke-width="3.5" :stroke-width="3.5"
size="20" size="20"
t-tooltip="interface.delete_checked" t-tooltip="interface.flush_db"
@click="onDeleteChecked" /> @click="onFlush" />
<div class="flex-item-expand ellipsis" style="text-align: center; margin: 0 5px">
<n-text>{{ checkedTip }}</n-text>
</div>
<icon-button <icon-button
:button-class="['nav-pane-func-btn']" :button-class="['nav-pane-func-btn']"
:icon="Close" :icon="Unlink"
:stroke-width="3.5" :stroke-width="3.5"
size="20" size="20"
t-tooltip="interface.quit_check_mode" t-tooltip="interface.disconnect"
@click="inCheckState = false" /> @click="onDisconnect" />
</div> </div>
</transition>
</div> </div>
</div> </div>
</template> </template>
@ -359,29 +299,17 @@ onMounted(() => onReload())
:deep(.toggle-btn) { :deep(.toggle-btn) {
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-radius: 3px;
padding: 4px;
} }
:deep(.toggle-on) { :deep(.filter-on) {
border-color: v-bind('themeVars.iconColorDisabled'); border-color: v-bind('themeVars.iconColorDisabled');
background-color: v-bind('themeVars.iconColorDisabled'); background-color: v-bind('themeVars.iconColorDisabled');
} }
:deep(.toggle-off) { :deep(.filter-off) {
border-color: #0000; 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 { .nav-pane-top {
//@include bottom-shadow(0.1); //@include bottom-shadow(0.1);
color: v-bind('themeVars.iconColor'); color: v-bind('themeVars.iconColor');

View File

@ -1,11 +1,11 @@
<script setup> <script setup>
import { computed, h, nextTick, reactive, ref, watchEffect } from 'vue' import { computed, h, nextTick, reactive, ref } from 'vue'
import { ConnectionType } from '@/consts/connection_type.js' import { ConnectionType } from '@/consts/connection_type.js'
import { NIcon, NSpace, useThemeVars } from 'naive-ui' import { NIcon, NSpace, useThemeVars } 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 Database from '@/components/icons/Database.vue' import Database from '@/components/icons/Database.vue'
import { filter, find, get, includes, indexOf, isEmpty, map, remove, size, startsWith } 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'
@ -26,40 +26,21 @@ import { useRender } from '@/utils/render.js'
const props = defineProps({ const props = defineProps({
server: String, server: String,
db: Number,
keyView: String, keyView: String,
loading: Boolean, loading: Boolean,
pattern: String, pattern: String,
fullLoaded: Boolean, fullLoaded: Boolean,
checkMode: Boolean,
checkedCount: Number,
}) })
const emit = defineEmits(['update:checked-count'])
const themeVars = useThemeVars() const themeVars = useThemeVars()
const render = useRender() const render = useRender()
const i18n = useI18n() const i18n = useI18n()
const expandedKeys = ref([]) const expandedKeys = ref([props.server])
const checkedKeys = reactive({
keys: [],
redisKeys: [],
})
const connectionStore = useConnectionStore() const connectionStore = useConnectionStore()
const browserStore = useBrowserStore() const browserStore = useBrowserStore()
const tabStore = useTabStore() const tabStore = useTabStore()
const dialogStore = useDialogStore() const dialogStore = useDialogStore()
watchEffect(
() => {
if (!props.checkMode) {
resetCheckedKey()
}
emit('update:checked-count', size(checkedKeys.keys))
},
{ flush: 'post' },
)
/** /**
* *
* @type {ComputedRef<string[]>} * @type {ComputedRef<string[]>}
@ -67,9 +48,9 @@ watchEffect(
const selectedKeys = computed(() => { const selectedKeys = computed(() => {
const tab = find(tabStore.tabList, { name: props.server }) const tab = find(tabStore.tabList, { name: props.server })
if (tab != null) { if (tab != null) {
return get(tab, 'selectedKeys', []) return get(tab, 'selectedKeys', [props.server])
} }
return [] return [props.server]
}) })
const data = computed(() => { const data = computed(() => {
@ -189,11 +170,6 @@ const resetExpandKey = (server, db, includeDB) => {
}) })
} }
const resetCheckedKey = () => {
checkedKeys.keys = []
checkedKeys.redisKeys = []
}
const handleSelectContextMenu = (key) => { const handleSelectContextMenu = (key) => {
contextMenuParam.show = false contextMenuParam.show = false
const selectedKey = get(selectedKeys.value, 0) const selectedKey = get(selectedKeys.value, 0)
@ -271,32 +247,6 @@ const handleSelectContextMenu = (key) => {
} }
} }
const onUpdateSelectedKeys = (keys, options) => {
try {
if (!isEmpty(options)) {
// prevent load duplicate key
for (const node of options) {
if (node.type === ConnectionType.RedisValue) {
const { key, db } = node
const redisKey = node.redisKeyCode || node.redisKey
if (!includes(selectedKeys.value, key)) {
browserStore.loadKeySummary({
server: props.server,
db,
key: redisKey,
})
}
return
}
}
}
// default is load blank key to display server status
tabStore.openBlank(props.server)
} finally {
tabStore.setSelectedKeys(props.server, keys)
}
}
const onUpdateExpanded = (value, option, meta) => { const onUpdateExpanded = (value, option, meta) => {
expandedKeys.value = value expandedKeys.value = value
if (!meta.node) { if (!meta.node) {
@ -323,17 +273,30 @@ const onUpdateExpanded = (value, option, meta) => {
} }
} }
/** const onUpdateSelectedKeys = (keys, options) => {
* try {
* @param {string[]} keys if (!isEmpty(options)) {
* @param {TreeOption[]} options // prevent load duplicate key
*/ for (const node of options) {
const onUpdateCheckedKeys = (keys, options) => { if (node.type === ConnectionType.RedisValue) {
checkedKeys.keys = keys const { key, db } = node
checkedKeys.redisKeys = map( const redisKey = node.redisKeyCode || node.redisKey
filter(options, (o) => o.type === ConnectionType.RedisValue), if (!includes(selectedKeys.value, key)) {
(o) => o.redisKeyCode || o.redisKey, browserStore.loadKeySummary({
) server: props.server,
db,
key: redisKey,
})
}
return
}
}
}
// default is load blank key to display server status
tabStore.openBlank(props.server)
} finally {
tabStore.setSelectedKeys(props.server, keys)
}
} }
const renderPrefix = ({ option }) => { const renderPrefix = ({ option }) => {
@ -486,10 +449,8 @@ const nodeProps = ({ option }) => {
console.warn('TODO: alert to ignore double click when loading') console.warn('TODO: alert to ignore double click when loading')
return return
} }
if (!props.checkMode) {
// default handle is expand current node // default handle is expand current node
nextTick().then(() => expandKey(option.key)) nextTick().then(() => expandKey(option.key))
}
}, },
onContextmenu(e) { onContextmenu(e) {
e.preventDefault() e.preventDefault()
@ -523,13 +484,6 @@ defineExpose({
resetExpandKey, resetExpandKey,
refreshTree: () => { refreshTree: () => {
treeKey.value = Date.now() treeKey.value = Date.now()
expandedKeys.value = []
resetCheckedKey()
},
deleteCheckedItems: () => {
if (!isEmpty(checkedKeys.redisKeys)) {
dialogStore.openDeleteKeyDialog(props.server, props.db, checkedKeys.redisKeys)
}
}, },
}) })
</script> </script>
@ -545,9 +499,6 @@ defineExpose({
:block-line="true" :block-line="true"
:block-node="true" :block-node="true"
:cancelable="false" :cancelable="false"
:cascade="true"
:checkable="props.checkMode"
:checked-keys="checkedKeys.keys"
:data="data" :data="data"
:expand-on-click="false" :expand-on-click="false"
:expanded-keys="expandedKeys" :expanded-keys="expandedKeys"
@ -559,12 +510,10 @@ defineExpose({
:render-suffix="renderSuffix" :render-suffix="renderSuffix"
:selected-keys="selectedKeys" :selected-keys="selectedKeys"
:show-irrelevant-nodes="false" :show-irrelevant-nodes="false"
check-strategy="child"
class="fill-height" class="fill-height"
virtual-scroll virtual-scroll
@update:selected-keys="onUpdateSelectedKeys" @update:selected-keys="onUpdateSelectedKeys"
@update:expanded-keys="onUpdateExpanded" @update:expanded-keys="onUpdateExpanded" />
@update:checked-keys="onUpdateCheckedKeys" />
<n-dropdown <n-dropdown
:options="contextMenuParam.options" :options="contextMenuParam.options"
:render-label="renderContextLabel" :render-label="renderContextLabel"

View File

@ -42,8 +42,7 @@
"auto_check_update": "Automatically check for updates" "auto_check_update": "Automatically check for updates"
}, },
"editor": { "editor": {
"name": "Editor", "name": "Editor"
"show_linenum": "Show Line Numbers"
} }
}, },
"interface": { "interface": {
@ -70,9 +69,6 @@
"delete_key": "Delete Key", "delete_key": "Delete Key",
"batch_delete_key": "Batch Delete Keys", "batch_delete_key": "Batch Delete Keys",
"flush_db": "Flush Database", "flush_db": "Flush Database",
"check_mode": "Check Mode",
"quit_check_mode": "Quit Check Mode",
"delete_checked": "Delete Checked Items",
"copy_value": "Copy Value", "copy_value": "Copy Value",
"edit_value": "Edit Value", "edit_value": "Edit Value",
"save_update": "Save Update", "save_update": "Save Update",
@ -136,8 +132,6 @@
"remove_tip": "{type} \"{name}\" will be deleted", "remove_tip": "{type} \"{name}\" will be deleted",
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted", "remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
"delete_key_succ": "\"{key}\" has been deleted", "delete_key_succ": "\"{key}\" has been deleted",
"deleting_key": "Deleting key: {key} ({index}/{count})",
"delete_completed": "Deletion process has been completed, {success} successed, {fail} failed",
"rename_binary_key_fail": "Rename binary key name is unsupported", "rename_binary_key_fail": "Rename binary key name is unsupported",
"handle_succ": "Success!", "handle_succ": "Success!",
"reload_succ": "Reloaded!", "reload_succ": "Reloaded!",

View File

@ -42,8 +42,7 @@
"auto_check_update": "自动检查更新" "auto_check_update": "自动检查更新"
}, },
"editor": { "editor": {
"name": "编辑器", "name": "编辑器"
"show_linenum": "显示行号"
} }
}, },
"interface": { "interface": {
@ -70,9 +69,6 @@
"delete_key": "删除键", "delete_key": "删除键",
"batch_delete_key": "批量删除键", "batch_delete_key": "批量删除键",
"flush_db": "清空数据库", "flush_db": "清空数据库",
"check_mode": "勾选模式",
"quit_check_mode": "退出勾选模式",
"delete_checked": "删除勾选项",
"copy_value": "复制值", "copy_value": "复制值",
"edit_value": "修改值", "edit_value": "修改值",
"save_update": "保存修改", "save_update": "保存修改",
@ -136,8 +132,6 @@
"remove_tip": "{type} \"{name}\" 将会被删除", "remove_tip": "{type} \"{name}\" 将会被删除",
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除", "remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
"delete_key_succ": "{key} 已被删除", "delete_key_succ": "{key} 已被删除",
"deleting_key": "正在删除键:{key} ({index}/{count})",
"delete_completed": "已完成删除操作,成功{success}个,失败{fail}个",
"rename_binary_key_fail": "不支持重命名二进制键名", "rename_binary_key_fail": "不支持重命名二进制键名",
"handle_succ": "操作成功", "handle_succ": "操作成功",
"reload_succ": "已重新载入", "reload_succ": "已重新载入",

View File

@ -25,7 +25,6 @@ import {
CloseConnection, CloseConnection,
ConvertValue, ConvertValue,
DeleteKey, DeleteKey,
DeleteOneKey,
FlushDB, FlushDB,
GetCmdHistory, GetCmdHistory,
GetKeyDetail, GetKeyDetail,
@ -54,7 +53,6 @@ import { ConnectionType } from '@/consts/connection_type.js'
import useConnectionStore from 'stores/connections.js' import useConnectionStore from 'stores/connections.js'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js' import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import { isRedisGlob } from '@/utils/glob_pattern.js' import { isRedisGlob } from '@/utils/glob_pattern.js'
import { i18nGlobal } from '@/utils/i18n.js'
const useBrowserStore = defineStore('browser', { const useBrowserStore = defineStore('browser', {
/** /**
@ -1843,46 +1841,43 @@ const useBrowserStore = defineStore('browser', {
}, },
/** /**
* delete multiple keys * delete keys with prefix
* @param {string} server * @param {string} connName
* @param {number} db * @param {number} db
* @param {string[]|number[][]} keys * @param {string} prefix
* @return {Promise<void>} * @param {boolean} async
* @returns {Promise<boolean>}
*/ */
async deleteKeys(server, db, keys) { async deleteKeyPrefix(connName, db, prefix, async) {
const delMsgRef = $message.loading('', { duration: 0 }) if (isEmpty(prefix)) {
let progress = 0 return false
let count = size(keys) }
let deletedCount = 0, try {
failCount = 0 if (!endsWith(prefix, '*')) {
for (const key of keys) { prefix += '*'
delMsgRef.content = i18nGlobal.t('dialogue.deleting_key', { }
key: decodeRedisKey(key), const { data, success, msg } = await DeleteKey(connName, db, prefix, async)
index: ++progress,
count,
})
const { success } = await DeleteOneKey(server, db, key)
if (success) { if (success) {
this._deleteKeyNode(server, db, key, false) // const { deleted: keys = [] } = data
deletedCount += 1 // for (const key of keys) {
} else { // await this._deleteKeyNode(connName, db, key)
failCount += 1 // }
const deleteCount = get(data, 'deleteCount', 0)
const separator = this._getSeparator(connName)
if (endsWith(prefix, '*')) {
prefix = prefix.substring(0, prefix.length - 1)
} }
if (endsWith(prefix, separator)) {
prefix = prefix.substring(0, prefix.length - 1)
} }
delMsgRef.destroy() this._deleteKeyNode(connName, db, prefix, true)
// refresh model data this._tidyNode(connName, db, prefix, true)
this._tidyNode(server, db, '', true) this._updateDBMaxKeys(connName, db, -deleteCount)
this._updateDBMaxKeys(server, db, -deletedCount) return true
if (failCount <= 0) {
// no fail
$message.success(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
} else if (failCount >= deletedCount) {
// all fail
$message.error(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
} else {
// some fail
$message.warn(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
} }
} finally {
}
return false
}, },
/** /**

View File

@ -1,5 +1,5 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia'
import useConnectionStore from "./connections.js"; import useConnectionStore from './connections.js'
/** /**
* connection dialog type * connection dialog type
@ -160,12 +160,6 @@ const useDialogStore = defineStore('dialog', {
this.renameDialogVisible = false this.renameDialogVisible = false
}, },
/**
*
* @param {string} server
* @param {number} db
* @param {string | string[]} key
*/
openDeleteKeyDialog(server, db, key) { openDeleteKeyDialog(server, db, key) {
this.deleteKeyParam.server = server this.deleteKeyParam.server = server
this.deleteKeyParam.db = db this.deleteKeyParam.db = db

View File

@ -36,7 +36,6 @@ const usePreferencesStore = defineStore('preferences', {
asideWidth: 300, asideWidth: 300,
windowWidth: 0, windowWidth: 0,
windowHeight: 0, windowHeight: 0,
windowMaximised: false,
}, },
general: { general: {
theme: 'auto', theme: 'auto',
@ -46,13 +45,12 @@ const usePreferencesStore = defineStore('preferences', {
scanSize: 3000, scanSize: 3000,
useSysProxy: false, useSysProxy: false,
useSysProxyHttp: false, useSysProxyHttp: false,
checkUpdate: true, checkUpdate: false,
skipVersion: '', skipVersion: '',
}, },
editor: { editor: {
font: '', font: '',
fontSize: 14, fontSize: 14,
showLineNum: false,
}, },
lastPref: {}, lastPref: {},
fontList: [], fontList: [],
@ -183,10 +181,6 @@ const usePreferencesStore = defineStore('preferences', {
autoCheckUpdate() { autoCheckUpdate() {
return get(this.general, 'checkUpdate', false) return get(this.general, 'checkUpdate', false)
}, },
showLineNum() {
return get(this.editor, 'showLineNum', true)
},
}, },
actions: { actions: {
_applyPreferences(data) { _applyPreferences(data) {
@ -204,11 +198,6 @@ const usePreferencesStore = defineStore('preferences', {
if (success) { if (success) {
this.lastPref = cloneDeep(data) this.lastPref = cloneDeep(data)
this._applyPreferences(data) this._applyPreferences(data)
// default value
const showLineNum = get(data, 'editor.showLineNum')
if (showLineNum === undefined) {
set(data, 'editor.showLineNum', true)
}
i18nGlobal.locale.value = this.currentLanguage i18nGlobal.locale.value = this.currentLanguage
} }
}, },

View File

@ -10,7 +10,6 @@ const useTabStore = defineStore('tab', {
* @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
* @property {string[]} checkdeKeys
* @property {string} [type] key type * @property {string} [type] key type
* @property {*} [value] key value * @property {*} [value] key value
* @property {string} [server] server name * @property {string} [server] server name
@ -639,7 +638,7 @@ const useTabStore = defineStore('tab', {
}, },
/** /**
* set selected keys in current display browser tree * set selected keys of current display browser tree
* @param {string} server * @param {string} server
* @param {string|string[]} [keys] * @param {string|string[]} [keys]
*/ */
@ -648,7 +647,7 @@ const useTabStore = defineStore('tab', {
if (tab != null) { if (tab != null) {
if (keys == null) { if (keys == null) {
// select nothing // select nothing
tab.selectedKeys = [] tab.selectedKeys = [server]
} else if (typeof keys === 'string') { } else if (typeof keys === 'string') {
tab.selectedKeys = [keys] tab.selectedKeys = [keys]
} else { } else {