Compare commits

...

6 Commits

22 changed files with 286 additions and 198 deletions

View File

@ -1,7 +1,7 @@
<script setup>
import ContentPane from './components/content/ContentPane.vue'
import BrowserPane from './components/sidebar/BrowserPane.vue'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { computed, onMounted, reactive, ref, watchEffect } from 'vue'
import { debounce } from 'lodash'
import { useThemeVars } from 'naive-ui'
import Ribbon from './components/sidebar/Ribbon.vue'
@ -43,14 +43,11 @@ const handleResize = () => {
saveSidebarWidth()
}
watch(
() => tabStore.nav,
(nav) => {
if (nav === 'log') {
logPaneRef.value?.refresh()
}
},
)
watchEffect(() => {
if (tabStore.nav === 'log') {
logPaneRef.value?.refresh()
}
})
const logoWrapperWidth = computed(() => {
return `${data.navMenuWidth + prefStore.behavior.asideWidth - 4}px`

View File

@ -90,6 +90,7 @@ defineExpose({
v-model:value="inputData.filter"
:placeholder="$t('interface.filter')"
:size="props.small ? 'small' : ''"
:theme-overrides="{ paddingSmall: '0 3px', paddingMedium: '0 6px' }"
clearable
@clear="onClearFilter"
@input="onInput"

View File

@ -1,5 +1,5 @@
<script setup>
import { computed, reactive, watch } from 'vue'
import { computed, reactive, watchEffect } from 'vue'
import { types } from '@/consts/support_redis_type.js'
import useDialog from 'stores/dialog'
import NewStringValue from '@/components/new_value/NewStringValue.vue'
@ -63,21 +63,18 @@ const title = computed(() => {
})
const dialogStore = useDialog()
watch(
() => dialogStore.addFieldsDialogVisible,
(visible) => {
if (visible) {
const { server, db, key, keyCode, type } = dialogStore.addFieldParam
newForm.server = server
newForm.db = db
newForm.key = key
newForm.keyCode = keyCode
newForm.type = type
newForm.opType = 0
newForm.value = null
}
},
)
watchEffect(() => {
if (dialogStore.addFieldsDialogVisible) {
const { server, db, key, keyCode, type } = dialogStore.addFieldParam
newForm.server = server
newForm.db = db
newForm.key = key
newForm.keyCode = keyCode
newForm.type = type
newForm.opType = 0
newForm.value = null
}
})
const browserStore = useBrowserStore()
const tab = useTabStore()
@ -222,7 +219,7 @@ const onClose = () => {
:is="addValueComponent[newForm.type]"
v-model:type="newForm.opType"
v-model:value="newForm.value" />
<n-form-item label=" " path="key" required>
<n-form-item :show-label="false" path="key" required>
<n-checkbox v-model:checked="newForm.reload">
{{ $t('dialogue.field.reload_when_succ') }}
</n-checkbox>

View File

@ -289,6 +289,7 @@ const onClose = () => {
v-model:value="generalForm.port"
:max="65535"
:min="1"
:show-button="false"
style="width: 200px" />
</n-form-item-gi>
<n-form-item-gi :label="$t('dialogue.connection.pwd')" :span="12" path="password">
@ -336,7 +337,12 @@ const onClose = () => {
:label="$t('dialogue.connection.advn.conn_timeout')"
:span="12"
path="connTimeout">
<n-input-number v-model:value="generalForm.connTimeout" :max="999999" :min="1">
<n-input-number
v-model:value="generalForm.connTimeout"
:max="999999"
:min="1"
:show-button="false"
style="width: 100%">
<template #suffix>
{{ $t('common.second') }}
</template>
@ -346,7 +352,12 @@ const onClose = () => {
:label="$t('dialogue.connection.advn.exec_timeout')"
:span="12"
path="execTimeout">
<n-input-number v-model:value="generalForm.execTimeout" :max="999999" :min="1">
<n-input-number
v-model:value="generalForm.execTimeout"
:max="999999"
:min="1"
:show-button="false"
style="width: 100%">
<template #suffix>
{{ $t('common.second') }}
</template>
@ -363,7 +374,11 @@ const onClose = () => {
</n-radio-group>
</n-form-item-gi>
<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"
:show-button="false"
style="width: 100%" />
</n-form-item-gi>
<n-form-item-gi :label="$t('dialogue.connection.advn.dbfilter_type')" :span="24">
<n-radio-group
@ -470,6 +485,7 @@ const onClose = () => {
v-model:value="generalForm.ssh.port"
:max="65535"
:min="1"
:show-button="false"
style="width: 200px" />
</n-form-item>
<n-form-item :label="$t('dialogue.connection.ssh.login_type')">

View File

@ -1,5 +1,5 @@
<script setup>
import { computed, nextTick, reactive, ref, watch } from 'vue'
import { computed, nextTick, reactive, ref, watchEffect } from 'vue'
import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n'
import { isEmpty, map, size } from 'lodash'
@ -18,28 +18,26 @@ const deleteForm = reactive({
const dialogStore = useDialog()
const browserStore = useBrowserStore()
watch(
() => dialogStore.deleteKeyDialogVisible,
(visible) => {
if (visible) {
const { server, db, key } = dialogStore.deleteKeyParam
deleteForm.server = server
deleteForm.db = db
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.affectedKeys = []
}
watchEffect(() => {
if (dialogStore.deleteKeyDialogVisible) {
const { server, db, key } = dialogStore.deleteKeyParam
deleteForm.server = server
deleteForm.db = db
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.affectedKeys = []
}
},
)
}
})
const loading = ref(false)
const deleting = ref(false)

View File

@ -1,5 +1,5 @@
<script setup>
import { reactive, ref, watch } from 'vue'
import { reactive, ref, watchEffect } from 'vue'
import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n'
import useBrowserStore from 'stores/browser.js'
@ -14,19 +14,17 @@ const flushForm = reactive({
const dialogStore = useDialog()
const browserStore = useBrowserStore()
watch(
() => dialogStore.flushDBDialogVisible,
(visible) => {
if (visible) {
const { server, db } = dialogStore.flushDBParam
flushForm.server = server
flushForm.db = db
flushForm.async = true
flushForm.confirm = false
loading.value = false
}
},
)
watchEffect(() => {
if (dialogStore.flushDBDialogVisible) {
const { server, db } = dialogStore.flushDBParam
flushForm.server = server
flushForm.db = db
flushForm.async = true
flushForm.confirm = false
loading.value = false
}
})
const loading = ref(false)
const i18n = useI18n()

View File

@ -1,5 +1,5 @@
<script setup>
import { computed, reactive, ref, watch } from 'vue'
import { computed, reactive, ref, watchEffect } from 'vue'
import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n'
import useConnectionStore from 'stores/connections.js'
@ -36,14 +36,11 @@ const isRenameMode = computed(() => !isEmpty(editGroup.value))
const dialogStore = useDialog()
const connectionStore = useConnectionStore()
watch(
() => dialogStore.groupDialogVisible,
(visible) => {
if (visible) {
groupForm.name = editGroup.value = dialogStore.editGroup
}
},
)
watchEffect(() => {
if (dialogStore.groupDialogVisible) {
groupForm.name = editGroup.value = dialogStore.editGroup
}
})
const i18n = useI18n()
const onConfirm = async () => {

View File

@ -10,6 +10,7 @@ const importKeyForm = reactive({
server: '',
db: 0,
expire: true,
reload: true,
file: '',
type: 0,
conflict: 0,
@ -25,6 +26,7 @@ watchEffect(() => {
importKeyForm.server = server
importKeyForm.db = db
importKeyForm.expire = true
importKeyForm.reload = true
importKeyForm.file = ''
importKeyForm.type = 0
importKeyForm.conflict = 0
@ -33,7 +35,7 @@ watchEffect(() => {
})
const i18n = useI18n()
const conflictOption = [
const conflictOption = computed(() => [
{
value: 0,
label: i18n.t('dialogue.import.conflict_overwrite'),
@ -42,7 +44,7 @@ const conflictOption = [
value: 1,
label: i18n.t('dialogue.import.conflict_ignore'),
},
]
])
const importEnable = computed(() => {
return !isEmpty(importKeyForm.file)
@ -51,8 +53,8 @@ const importEnable = computed(() => {
const onConfirmImport = async () => {
try {
importing.value = true
const { server, db, file, conflict, expire } = importKeyForm
browserStore.importKeysFromCSVFile(server, db, file, conflict, expire).catch((e) => {})
const { server, db, file, conflict, expire, reload } = importKeyForm
browserStore.importKeysFromCSVFile(server, db, file, conflict, expire, reload).catch((e) => {})
} catch (e) {
$message.error(e.message)
return
@ -93,11 +95,6 @@ const onClose = () => {
:placeholder="$t('dialogue.import.open_csv_file_tip')"
ext="csv" />
</n-form-item>
<n-form-item :label="$t('dialogue.import.import_expire_title')">
<n-checkbox v-model:checked="importKeyForm.expire" :autofocus="false">
{{ $t('dialogue.import.import_expire') }}
</n-checkbox>
</n-form-item>
<n-form-item :label="$t('dialogue.import.conflict_handle')">
<n-radio-group v-model:value="importKeyForm.conflict">
<n-radio-button
@ -107,6 +104,16 @@ const onClose = () => {
:value="op.value" />
</n-radio-group>
</n-form-item>
<n-form-item :label="$t('dialogue.import.import_expire_title')" :show-label="false">
<n-space :wrap-item="false">
<n-checkbox v-model:checked="importKeyForm.expire" :autofocus="false">
{{ $t('dialogue.import.import_expire') }}
</n-checkbox>
<n-checkbox v-model:checked="importKeyForm.reload" :autofocus="false">
{{ $t('dialogue.import.reload') }}
</n-checkbox>
</n-space>
</n-form-item>
</n-form>
</n-spin>

View File

@ -1,5 +1,5 @@
<script setup>
import { computed, reactive, ref, watch } from 'vue'
import { computed, reactive, ref, watchEffect } from 'vue'
import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n'
import { types } from '@/consts/support_redis_type.js'
@ -26,18 +26,16 @@ const typeOptions = computed(() => {
})
const dialogStore = useDialog()
watch(
() => dialogStore.keyFilterDialogVisible,
(visible) => {
if (visible) {
const { server, db, type, pattern } = dialogStore.keyFilterParam
filterForm.server = server
filterForm.db = db || 0
filterForm.type = type || ''
filterForm.pattern = pattern || '*'
}
},
)
watchEffect(() => {
if (dialogStore.keyFilterDialogVisible) {
const { server, db, type, pattern } = dialogStore.keyFilterParam
filterForm.server = server
filterForm.db = db || 0
filterForm.type = type || ''
filterForm.pattern = pattern || '*'
}
})
const browserStore = useBrowserStore()
const onConfirm = () => {}

View File

@ -1,5 +1,5 @@
<script setup>
import { computed, h, nextTick, reactive, ref, watch } from 'vue'
import { computed, h, nextTick, reactive, ref, watchEffect } from 'vue'
import { types, typesColor } from '@/consts/support_redis_type.js'
import useDialog from 'stores/dialog'
import { endsWith, get, isEmpty, keys, map, trim } from 'lodash'
@ -64,29 +64,26 @@ const defaultValue = {
}
const dialogStore = useDialog()
watch(
() => dialogStore.newKeyDialogVisible,
(visible) => {
if (visible) {
const { prefix, server, db } = dialogStore.newKeyParam
const separator = browserStore.getSeparator(server)
newForm.server = server
if (isEmpty(prefix)) {
newForm.key = ''
watchEffect(() => {
if (dialogStore.newKeyDialogVisible) {
const { prefix, server, db } = dialogStore.newKeyParam
const separator = browserStore.getSeparator(server)
newForm.server = server
if (isEmpty(prefix)) {
newForm.key = ''
} else {
if (!endsWith(prefix, separator)) {
newForm.key = prefix + separator
} else {
if (!endsWith(prefix, separator)) {
newForm.key = prefix + separator
} else {
newForm.key = prefix
}
newForm.key = prefix
}
newForm.db = db
newForm.type = options.value[0].value
newForm.ttl = -1
newForm.value = null
}
},
)
newForm.db = db
newForm.type = options.value[0].value
newForm.ttl = -1
newForm.value = null
}
})
const renderTypeLabel = (option) => {
return h(
@ -210,6 +207,7 @@ const onClose = () => {
v-model:value="newForm.ttl"
:max="Number.MAX_SAFE_INTEGER"
:min="-1"
:show-button="false"
placeholder="TTL">
<template #suffix>
{{ $t('common.second') }}

View File

@ -101,7 +101,11 @@ const onClose = () => {
<n-input-number v-model:value="prefStore.general.fontSize" :max="65535" :min="1" />
</n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.scan_size')" :span="12">
<n-input-number v-model:value="prefStore.general.scanSize" :min="1" />
<n-input-number
v-model:value="prefStore.general.scanSize"
:min="1"
:show-button="false"
style="width: 100%" />
</n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.key_icon_style')" :span="12">
<n-select v-model:value="prefStore.general.keyIconStyle" :options="keyOptions" />
@ -128,13 +132,13 @@ const onClose = () => {
<n-tab-pane :tab="$t('preferences.editor.name')" display-directive="show" name="editor">
<n-form :disabled="loading" :model="prefStore.editor" :show-require-mark="false" label-placement="top">
<n-grid :x-gap="10">
<n-form-item-gi :label="$t('preferences.general.font')" :span="24" required>
<n-form-item-gi :label="$t('preferences.general.font')" :span="12" required>
<n-select
v-model:value="prefStore.editor.font"
:options="prefStore.fontOption"
filterable />
</n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.font_size')" :show-feedback="false" :span="24">
<n-form-item-gi :label="$t('preferences.general.font_size')" :show-feedback="false" :span="12">
<n-input-number v-model:value="prefStore.editor.fontSize" :max="65535" :min="1" />
</n-form-item-gi>
<n-form-item-gi :show-feedback="false" :span="24">

View File

@ -1,5 +1,5 @@
<script setup>
import { reactive, watch } from 'vue'
import { reactive, watchEffect } from 'vue'
import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n'
import useBrowserStore from 'stores/browser.js'
@ -15,18 +15,16 @@ const renameForm = reactive({
const dialogStore = useDialog()
const browserStore = useBrowserStore()
const tab = useTabStore()
watch(
() => dialogStore.renameDialogVisible,
(visible) => {
if (visible) {
const { server, db, key } = dialogStore.renameKeyParam
renameForm.server = server
renameForm.db = db
renameForm.key = key
renameForm.newKey = key
}
},
)
watchEffect(() => {
if (dialogStore.renameDialogVisible) {
const { server, db, key } = dialogStore.renameKeyParam
renameForm.server = server
renameForm.db = db
renameForm.key = key
renameForm.newKey = key
}
})
const i18n = useI18n()
const onRename = async () => {
@ -57,9 +55,9 @@ const onClose = () => {
:closable="false"
:close-on-esc="false"
:mask-closable="false"
:negative-button-props="{ size: 'medium' }"
:negative-button-props="{ focusable: false, size: 'medium' }"
:negative-text="$t('common.cancel')"
:positive-button-props="{ size: 'medium' }"
:positive-button-props="{ focusable: false, size: 'medium' }"
:positive-text="$t('common.confirm')"
:show-icon="false"
:title="$t('interface.rename_key')"

View File

@ -1,10 +1,11 @@
<script setup>
import { reactive, watch } from 'vue'
import { computed, reactive, watchEffect } from 'vue'
import useDialog from 'stores/dialog'
import useTabStore from 'stores/tab.js'
import Binary from '@/components/icons/Binary.vue'
import { isEmpty } from 'lodash'
import useBrowserStore from 'stores/browser.js'
import { useI18n } from 'vue-i18n'
const ttlForm = reactive({
server: '',
@ -12,33 +13,62 @@ const ttlForm = reactive({
key: '',
keyCode: null,
ttl: -1,
unit: 1,
})
const dialogStore = useDialog()
const browserStore = useBrowserStore()
const tabStore = useTabStore()
watch(
() => dialogStore.ttlDialogVisible,
(visible) => {
if (visible) {
// get ttl from current tab
const tab = tabStore.currentTab
if (tab != null) {
ttlForm.server = tab.name
ttlForm.db = tab.db
ttlForm.key = tab.key
ttlForm.keyCode = tab.keyCode
if (tab.ttl < 0) {
// forever
ttlForm.ttl = -1
} else {
ttlForm.ttl = tab.ttl
}
watchEffect(() => {
if (dialogStore.ttlDialogVisible) {
// get ttl from current tab
const tab = tabStore.currentTab
if (tab != null) {
ttlForm.server = tab.name
ttlForm.db = tab.db
ttlForm.key = tab.key
ttlForm.keyCode = tab.keyCode
ttlForm.unit = 1
if (tab.ttl < 0) {
// forever
ttlForm.ttl = -1
} else {
ttlForm.ttl = tab.ttl
}
}
}
})
const i18n = useI18n()
const unit = computed(() => [
{ value: 1, label: i18n.t('common.second') },
{
value: 60,
label: i18n.t('common.minute'),
},
)
{
value: 3600,
label: i18n.t('common.hour'),
},
{
value: 86400,
label: i18n.t('common.day'),
},
])
const quickOption = computed(() => [
{ value: -1, unit: 1, label: i18n.t('interface.forever') },
{ value: 10, unit: 1, label: `10 ${i18n.t('common.second')}` },
{ value: 1, unit: 60, label: `1 ${i18n.t('common.minute')}` },
{ value: 1, unit: 3600, label: `1 ${i18n.t('common.hour')}` },
{ value: 1, unit: 86400, label: `1 ${i18n.t('common.day')}` },
])
const onQuickSet = (opt) => {
ttlForm.ttl = opt.value
ttlForm.unit = opt.unit
}
const onClose = () => {
dialogStore.closeTTLDialog()
@ -51,13 +81,14 @@ const onConfirm = async () => {
return
}
const key = isEmpty(ttlForm.keyCode) ? ttlForm.key : ttlForm.keyCode
const success = await browserStore.setTTL(tab.name, tab.db, key, ttlForm.ttl)
const ttl = ttlForm.ttl * (ttlForm.unit || 1)
const success = await browserStore.setTTL(tab.name, tab.db, key, ttl)
if (success) {
tabStore.updateTTL({
server: ttlForm.server,
db: ttlForm.db,
key: ttlForm.key,
ttl: ttlForm.ttl,
ttl: ttl,
})
}
} catch (e) {
@ -74,6 +105,12 @@ const onConfirm = async () => {
:closable="false"
:close-on-esc="false"
:mask-closable="false"
:negative-button-props="{ focusable: false, size: 'medium' }"
:negative-text="$t('common.cancel')"
:on-negative-click="onClose"
:on-positive-click="onConfirm"
:positive-button-props="{ focusable: false, size: 'medium' }"
:positive-text="$t('common.save')"
:show-icon="false"
:title="$t('dialogue.ttl.title')"
preset="dialog"
@ -87,27 +124,30 @@ const onConfirm = async () => {
</n-input>
</n-form-item>
<n-form-item :label="$t('interface.ttl')" required>
<n-input-number
v-model:value="ttlForm.ttl"
:max="Number.MAX_SAFE_INTEGER"
:min="-1"
style="width: 100%">
<template #suffix>
{{ $t('common.second') }}
</template>
</n-input-number>
<n-input-group>
<n-input-number
v-model:value="ttlForm.ttl"
:max="Number.MAX_SAFE_INTEGER"
:min="-1"
:show-button="false"
class="flex-item-expand" />
<n-select v-model:value="ttlForm.unit" :options="unit" style="max-width: 150px" />
</n-input-group>
</n-form-item>
<n-form-item :label="$t('dialogue.ttl.quick_set')" :show-feedback="false">
<n-space :wrap="true" :wrap-item="false">
<n-button
v-for="(opt, i) in quickOption"
:key="i"
round
secondary
size="small"
@click="onQuickSet(opt)">
{{ opt.label }}
</n-button>
</n-space>
</n-form-item>
</n-form>
<template #action>
<div class="flex-item-expand">
<n-button :focusable="false" @click="ttlForm.ttl = -1">{{ $t('dialogue.key.persist_key') }}</n-button>
</div>
<div class="flex-item n-dialog__action">
<n-button :focusable="false" @click="onClose">{{ $t('common.cancel') }}</n-button>
<n-button :focusable="false" type="primary" @click="onConfirm">{{ $t('common.save') }}</n-button>
</div>
</template>
</n-modal>
</template>

View File

@ -72,6 +72,7 @@ const onUpdate = () => {
<n-input-number
v-model:value="value.score"
:placeholder="$t('dialogue.field.enter_score')"
:show-button="false"
@update:value="onUpdate" />
</template>
<template #action="{ index, create, remove, move }">

View File

@ -52,6 +52,7 @@ defineExpose({
<n-input-number
v-model:value="value.score"
:placeholder="$t('dialogue.field.enter_score')"
:show-button="false"
@update:value="onUpdate" />
</template>
<template #action="{ index, create, remove, move }">

View File

@ -3,7 +3,7 @@ import { useThemeVars } from 'naive-ui'
import BrowserTree from './BrowserTree.vue'
import IconButton from '@/components/common/IconButton.vue'
import useTabStore from 'stores/tab.js'
import { computed, nextTick, onMounted, reactive, ref, unref } from 'vue'
import { computed, nextTick, onMounted, reactive, ref, unref, watch } from 'vue'
import { find, get, map, size } from 'lodash'
import Refresh from '@/components/icons/Refresh.vue'
import useDialogStore from 'stores/dialog.js'
@ -238,6 +238,11 @@ const onSelectOptions = (select) => {
}
onMounted(() => onReload())
watch(
() => browserStore.getReloadKey(props.server),
(key) => onReload(),
)
// forbid dynamic switch key view due to performance issues
// const viewType = ref(0)
// const onSwitchView = (selectView) => {
@ -284,6 +289,7 @@ onMounted(() => onReload())
:options="addOptions"
placement="bottom-end"
style="min-width: 130px"
trigger="click"
@select="onSelectOptions">
<n-button :focusable="false" size="small" style="padding: 0 3px">
<n-icon size="10">

View File

@ -367,13 +367,7 @@ const renderPrefix = ({ option }) => {
case ConnectionType.RedisValue:
if (prefStore.keyIconType === typesIconStyle.ICON) {
return h(
NIcon,
{ size: 20 },
{
default: () => h(!!option.redisKeyCode ? Binary : Key),
},
)
return h(NIcon, { size: 20 }, () => h(Key))
}
const loading = isEmpty(option.redisType) || option.redisType === 'loading'
if (loading) {
@ -506,10 +500,8 @@ const calcValueMenu = () => {
// render menu function icon
const renderSuffix = ({ option }) => {
if (
(option.type === ConnectionType.RedisDB && option.opened) ||
(includes(selectedKeys.value, option.key) && !props.checkMode)
) {
const selected = includes(selectedKeys.value, option.key)
if (selected && !props.checkMode) {
switch (option.type) {
case ConnectionType.RedisDB:
return renderIconMenu(calcDBMenu(option.opened, option.loading, option.fullLoaded))
@ -518,6 +510,9 @@ const renderSuffix = ({ option }) => {
case ConnectionType.RedisValue:
return renderIconMenu(calcValueMenu())
}
} else if (!selected && !!option.redisKeyCode && option.type === ConnectionType.RedisValue) {
// render binary icon
return renderIconMenu(h(NIcon, { size: 20 }, () => h(Binary)))
}
return null
}

View File

@ -10,6 +10,9 @@
"update": "Update",
"none": "None",
"second": "Second(s)",
"minute": "Minutes(s)",
"hour": "Hour(s)",
"day": "Day(s)",
"unit_day": "D",
"unit_hour": "H",
"unit_minute": "M",
@ -274,6 +277,8 @@
},
"export": {
"name": "Export Data",
"export_expire_title": "Expiration",
"export_expire": "Include Expiration Time",
"export": "Export",
"save_file": "Export Path",
"save_file_tip": "Select the path to save exported file",
@ -282,19 +287,21 @@
},
"import": {
"name": "Import Data",
"export_expire_title": "Expiration",
"export_expire": "Export Expiration Time",
"import_expire_title": "Expiration",
"import_expire": "Import Expiration Time",
"import": "Import",
"reload": "Reload Keys After Imported",
"open_csv_file": "Import File",
"open_csv_file_tip": "Select the file for import",
"conflict_handle": "Handle Key Conflict",
"conflict_handle": "Key Conflict Resolution",
"conflict_overwrite": "Overwrite",
"conflict_ignore": "Ignore",
"importing": "Importing Keys imported/overwrite:{imported} conflict/fail:{conflict}",
"import_completed": "Import completed, {success} successes, {ignored} failed"
},
"ttl": {
"title": "Set Key TTL"
"title": "Set Key TTL",
"quick_set": "Quick Settings"
},
"upgrade": {
"title": "New Version Available",

View File

@ -10,6 +10,9 @@
"update": "更新",
"none": "无",
"second": "秒",
"minute": "分钟",
"hour": "小时",
"day": "天",
"unit_day": "天",
"unit_hour": "小时",
"unit_minute": "分钟",
@ -286,6 +289,7 @@
"name": "导入数据",
"import_expire_title": "过期时间",
"import_expire": "包含键过期时间",
"reload": "导入完成后重新载入",
"import": "确认导入",
"open_csv_file": "导入文件路径",
"open_csv_file_tip": "选择需要导入的文件",
@ -296,7 +300,8 @@
"import_completed": "已完成导入操作,成功{success}个,忽略{ignored}个"
},
"ttl": {
"title": "设置键存活时间"
"title": "设置键存活时间",
"quick_set": "快捷设置"
},
"upgrade": {
"title": "有可用新版本",

View File

@ -19,6 +19,7 @@ export class RedisServerState {
/**
* @param {string} name server name
* @param {number} db current opened database
* @param {number} reloadKey try to reload when changed
* @param {{}} stats current server status info
* @param {Object.<number, RedisDatabaseItem>} databases database list
* @param {string|null} patternFilter pattern filter
@ -40,6 +41,7 @@ export class RedisServerState {
}) {
this.name = name
this.db = db
this.reloadKey = Date.now()
this.stats = stats
this.databases = databases
this.patternFilter = patternFilter
@ -83,7 +85,7 @@ export class RedisServerState {
* @param {number} updateVal
*/
updateDBKeyCount(db, updateVal) {
const dbInst = this.databases[this.db]
const dbInst = this.databases[db]
if (dbInst != null) {
dbInst.maxKeys = Math.max(0, dbInst.maxKeys + updateVal)
}
@ -288,6 +290,11 @@ export class RedisServerState {
// clear all key nodes
this.nodeMap.clear()
this.getRoot()
const dbInst = this.databases[this.db]
if (dbInst != null) {
dbInst.maxKeys = 0
dbInst.keyCount = 0
}
} else {
const keyParts = split(key, this.separator)
const totalParts = size(keyParts)

View File

@ -166,6 +166,20 @@ const useBrowserStore = defineStore('browser', {
return get(rootNode, 'children', [])
},
getReloadKey(server) {
/** @type {RedisServerState} **/
const serverInst = this.servers[server]
return serverInst != null ? serverInst.reloadKey : 0
},
reloadServer(server) {
/** @type {RedisServerState} **/
const serverInst = this.servers[server]
if (serverInst != null) {
serverInst.reloadKey = Date.now()
}
},
/**
* switch key view
* @param {string} connName
@ -1493,7 +1507,7 @@ const useBrowserStore = defineStore('browser', {
*/
async deleteKey(server, db, key, soft) {
try {
let deleteCount = 0
let deleteCount = 1
if (soft !== true) {
const { data } = await DeleteKey(server, db, key)
deleteCount = get(data, 'deleteCount', 0)
@ -1660,9 +1674,10 @@ const useBrowserStore = defineStore('browser', {
* @param {string} path
* @param {number} conflict
* @param {boolean} [expire]
* @param {boolean} [reload]
* @return {Promise<void>}
*/
async importKeysFromCSVFile(server, db, path, conflict, expire) {
async importKeysFromCSVFile(server, db, path, conflict, expire, reload) {
const msgRef = $message.loading('', { duration: 0, closable: true })
let imported = 0
let ignored = 0
@ -1695,8 +1710,11 @@ const useBrowserStore = defineStore('browser', {
if (canceled) {
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
} else {
// no fail
// finish
$message.success(i18nGlobal.t('dialogue.import.import_completed', { success: imported, ignored }))
if (reload) {
this.reloadServer(server)
}
}
},
@ -1717,7 +1735,6 @@ const useBrowserStore = defineStore('browser', {
if (serverInst != null) {
// update tree view data
serverInst.removeKeyNode()
serverInst.setDBKeyCount(db, 0)
}
// set tab content empty
const tab = useTabStore()

View File

@ -23,7 +23,7 @@ export const themeOverrides = {
paddingMedium: '0 12px',
},
Tag: {
// borderRadius: '3px'
borderRadius: '4px',
heightLarge: '32px',
},
Input: {