Compare commits

..

No commits in common. "edaef2a78cba77307a132ae7ed98ec836c15596c" and "3c7003291c2d6ef71af9a8f9af4fc89f5d0da855" have entirely different histories.

16 changed files with 167 additions and 305 deletions

View File

@ -2093,34 +2093,41 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
defer cancelFunc() defer cancelFunc()
cancelEvent := "delete:stop:" + serialNo cancelEvent := "delete:stop:" + serialNo
cancelStopEvent := runtime.EventsOnce(ctx, cancelEvent, func(data ...any) { runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
cancelFunc() cancelFunc()
}) })
processEvent := "deleting:" + serialNo
total := len(ks) total := len(ks)
var failed atomic.Int64 var failed atomic.Int64
var canceled bool var canceled bool
var deletedKeys = make([]any, 0, total) var deletedKeys = make([]any, 0, total)
var mutex sync.Mutex var mutex sync.Mutex
del := func(ctx context.Context, cli redis.UniversalClient) error { del := func(ctx context.Context, cli redis.UniversalClient) error {
const batchSize = 1000 startTime := time.Now().Add(-10 * time.Second)
for i := 0; i < total; i += batchSize { for i, k := range ks {
pipe := cli.Pipeline() // emit progress per second
for j := 0; j < batchSize; j++ { if i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100 {
if i+j < total { startTime = time.Now()
pipe.Del(ctx, strutil.DecodeRedisKey(ks[i+j])) param := map[string]any{
"total": total,
"progress": i + 1,
"processing": k,
} }
runtime.EventsEmit(b.ctx, processEvent, param)
// do some sleep to prevent blocking the Redis server
time.Sleep(10 * time.Millisecond)
} }
cmders, delErr := pipe.Exec(ctx)
for j, cmder := range cmders { key := strutil.DecodeRedisKey(k)
if cmder.(*redis.IntCmd).Val() != 1 { delErr := cli.Del(ctx, key).Err()
if err != nil {
failed.Add(1) failed.Add(1)
} else { } else {
// save deleted key // save deleted key
mutex.Lock() mutex.Lock()
deletedKeys = append(deletedKeys, ks[i+j]) deletedKeys = append(deletedKeys, k)
mutex.Unlock() mutex.Unlock()
} }
}
if errors.Is(delErr, context.Canceled) || canceled { if errors.Is(delErr, context.Canceled) || canceled {
canceled = true canceled = true
break break
@ -2138,7 +2145,7 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
err = del(ctx, client) err = del(ctx, client)
} }
cancelStopEvent() runtime.EventsOff(ctx, cancelEvent)
resp.Success = true resp.Success = true
resp.Data = struct { resp.Data = struct {
Canceled bool `json:"canceled"` Canceled bool `json:"canceled"`
@ -2182,7 +2189,8 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
writer := csv.NewWriter(file) writer := csv.NewWriter(file)
defer writer.Flush() defer writer.Flush()
cancelStopEvent := runtime.EventsOnce(ctx, "export:stop:"+path, func(data ...any) { cancelEvent := "export:stop:" + path
runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
cancelFunc() cancelFunc()
}) })
processEvent := "exporting:" + path processEvent := "exporting:" + path
@ -2198,7 +2206,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
"progress": i + 1, "progress": i + 1,
"processing": k, "processing": k,
} }
runtime.EventsEmit(ctx, processEvent, param) runtime.EventsEmit(b.ctx, processEvent, param)
} }
key := strutil.DecodeRedisKey(k) key := strutil.DecodeRedisKey(k)
@ -2222,7 +2230,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
} }
} }
cancelStopEvent() runtime.EventsOff(ctx, cancelEvent)
resp.Success = true resp.Success = true
resp.Data = struct { resp.Data = struct {
Canceled bool `json:"canceled"` Canceled bool `json:"canceled"`
@ -2266,7 +2274,7 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
reader := csv.NewReader(file) reader := csv.NewReader(file)
cancelEvent := "import:stop:" + path cancelEvent := "import:stop:" + path
cancelStopEvent := runtime.EventsOnce(ctx, cancelEvent, func(data ...any) { runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
cancelFunc() cancelFunc()
}) })
processEvent := "importing:" + path processEvent := "importing:" + path
@ -2341,7 +2349,7 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
} }
} }
cancelStopEvent() runtime.EventsOff(ctx, cancelEvent)
resp.Success = true resp.Success = true
resp.Data = struct { resp.Data = struct {
Canceled bool `json:"canceled"` Canceled bool `json:"canceled"`

View File

@ -6,7 +6,6 @@ type Preferences struct {
Behavior PreferencesBehavior `json:"behavior" yaml:"behavior"` Behavior PreferencesBehavior `json:"behavior" yaml:"behavior"`
General PreferencesGeneral `json:"general" yaml:"general"` General PreferencesGeneral `json:"general" yaml:"general"`
Editor PreferencesEditor `json:"editor" yaml:"editor"` Editor PreferencesEditor `json:"editor" yaml:"editor"`
Cli PreferencesCli `json:"cli" yaml:"cli"`
} }
func NewPreferences() Preferences { func NewPreferences() Preferences {
@ -29,10 +28,6 @@ func NewPreferences() Preferences {
ShowLineNum: true, ShowLineNum: true,
ShowFolding: true, ShowFolding: true,
}, },
Cli: PreferencesCli{
FontSize: consts.DEFAULT_FONT_SIZE,
CursorStyle: "block",
},
} }
} }
@ -49,7 +44,6 @@ type PreferencesGeneral struct {
Theme string `json:"theme" yaml:"theme"` Theme string `json:"theme" yaml:"theme"`
Language string `json:"language" yaml:"language"` Language string `json:"language" yaml:"language"`
Font string `json:"font" yaml:"font,omitempty"` Font string `json:"font" yaml:"font,omitempty"`
FontFamily []string `json:"fontFamily" yaml:"font_family,omitempty"`
FontSize int `json:"fontSize" yaml:"font_size"` FontSize int `json:"fontSize" yaml:"font_size"`
ScanSize int `json:"scanSize" yaml:"scan_size"` ScanSize int `json:"scanSize" yaml:"scan_size"`
KeyIconStyle int `json:"keyIconStyle" yaml:"key_icon_style"` KeyIconStyle int `json:"keyIconStyle" yaml:"key_icon_style"`
@ -61,14 +55,7 @@ type PreferencesGeneral struct {
type PreferencesEditor struct { type PreferencesEditor struct {
Font string `json:"font" yaml:"font,omitempty"` Font string `json:"font" yaml:"font,omitempty"`
FontFamily []string `json:"fontFamily" yaml:"font_family,omitempty"`
FontSize int `json:"fontSize" yaml:"font_size"` FontSize int `json:"fontSize" yaml:"font_size"`
ShowLineNum bool `json:"showLineNum" yaml:"show_line_num"` ShowLineNum bool `json:"showLineNum" yaml:"show_line_num"`
ShowFolding bool `json:"showFolding" yaml:"show_folding"` ShowFolding bool `json:"showFolding" yaml:"show_folding"`
} }
type PreferencesCli struct {
FontFamily []string `json:"fontFamily" yaml:"font_family,omitempty"`
FontSize int `json:"fontSize" yaml:"font_size"`
CursorStyle string `json:"cursorStyle" yaml:"cursor_style,omitempty"`
}

View File

@ -32,12 +32,9 @@ let fitAddonInst = null
* @return {{fitAddon: xterm-addon-fit.FitAddon, term: Terminal}} * @return {{fitAddon: xterm-addon-fit.FitAddon, term: Terminal}}
*/ */
const newTerm = () => { const newTerm = () => {
const { fontSize = 14, fontFamily = 'Courier New' } = prefStore.cliFont
const term = new Terminal({ const term = new Terminal({
allowProposedApi: true, allowProposedApi: true,
fontFamily, fontSize: prefStore.general.fontSize || 14,
fontSize,
cursorStyle: prefStore.cli.cursorStyle || 'block',
cursorBlink: true, cursorBlink: true,
disableStdin: false, disableStdin: false,
screenReaderMode: true, screenReaderMode: true,
@ -93,21 +90,10 @@ defineExpose({
}) })
watch( watch(
() => prefStore.cliFont, () => prefStore.general.fontSize,
({ fontSize = 14, fontFamily = 'Courier New' }) => { (fontSize) => {
if (termInst != null) { if (termInst != null) {
termInst.options.fontSize = fontSize termInst.options.fontSize = fontSize
termInst.options.fontFamily = fontFamily
}
resizeTerm()
},
)
watch(
() => prefStore.cli.cursorStyle,
(style) => {
if (termInst != null) {
termInst.options.cursorStyle = style || 'block'
} }
resizeTerm() resizeTerm()
}, },

View File

@ -58,7 +58,7 @@ const readonlyValue = computed(() => {
const pref = usePreferencesStore() const pref = usePreferencesStore()
onMounted(async () => { onMounted(async () => {
if (editorRef.value != null) { if (editorRef.value != null) {
const { fontSize, fontFamily = ['monaco'] } = pref.editorFont const { fontSize, fontFamily = undefined } = pref.editorFont
editorNode = monaco.editor.create(editorRef.value, { editorNode = monaco.editor.create(editorRef.value, {
// value: props.content, // value: props.content,
theme: pref.isDark ? 'rdm-dark' : 'rdm-light', theme: pref.isDark ? 'rdm-dark' : 'rdm-light',
@ -151,11 +151,21 @@ watch(
) )
watch( watch(
() => pref.editorFont, () => pref.editorFont.fontSize,
({ fontSize, fontFamily }) => { (fontSize) => {
if (editorNode != null) { if (editorNode != null) {
editorNode.updateOptions({ editorNode.updateOptions({
fontSize, fontSize,
})
}
},
)
watch(
() => pref.editorFont.fontFamily,
(fontFamily = undefined) => {
if (editorNode != null) {
editorNode.updateOptions({
fontFamily, fontFamily,
}) })
} }

View File

@ -237,7 +237,7 @@ onMounted(() => {
pageState.refreshInterval = interval === 0 ? 5 : interval pageState.refreshInterval = interval === 0 ? 5 : interval
onToggleRefresh(true) onToggleRefresh(true)
} else { } else {
setTimeout(refreshInfo, 3000) setTimeout(refreshInfo, 5000)
// setTimeout(_mockChart, 1000) // setTimeout(_mockChart, 1000)
} }
refreshInfo() refreshInfo()

View File

@ -115,9 +115,11 @@ const onClose = () => {
required> 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-checkbox v-model:checked="deleteForm.async">--> <!-- <n-checkbox v-model:checked="deleteForm.async">-->
<!-- {{ $t('dialogue.key.silent') }}--> <!-- {{ $t('dialogue.key.async_delete_title') }}-->
<!-- </n-checkbox>--> <!-- </n-checkbox>-->
<!-- </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)})`"

View File

@ -5,7 +5,6 @@ import useDialog from 'stores/dialog'
import usePreferencesStore from 'stores/preferences.js' import usePreferencesStore from 'stores/preferences.js'
import { map, sortBy } from 'lodash' import { map, sortBy } from 'lodash'
import { typesIconStyle } from '@/consts/support_redis_type.js' import { typesIconStyle } from '@/consts/support_redis_type.js'
import Help from '@/components/icons/Help.vue'
const prefStore = usePreferencesStore() const prefStore = usePreferencesStore()
@ -93,27 +92,14 @@ const onClose = () => {
:render-label="({ label, value }) => (value === 'auto' ? $t(label) : label)" :render-label="({ label, value }) => (value === 'auto' ? $t(label) : label)"
filterable /> filterable />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="24" required> <n-form-item-gi :label="$t('preferences.general.font')" :span="12" required>
<template #label>
{{ $t('preferences.general.font') }}
<n-tooltip trigger="hover">
<template #trigger>
<n-icon :component="Help" />
</template>
<div class="text-block">
{{ $t('preferences.font_tip') }}
</div>
</n-tooltip>
</template>
<n-select <n-select
v-model:value="prefStore.general.fontFamily" v-model:value="prefStore.general.font"
:options="prefStore.fontOption" :options="prefStore.fontOption"
:render-label="({ label, value }) => (value === '' ? $t(label) : label)" :render-label="({ label, value }) => (value === '' ? $t(label) : label)"
filterable filterable />
multiple
tag />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.font_size')" :span="24"> <n-form-item-gi :label="$t('preferences.general.font_size')" :span="12">
<n-input-number v-model:value="prefStore.general.fontSize" :max="65535" :min="1" /> <n-input-number v-model:value="prefStore.general.fontSize" :max="65535" :min="1" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.scan_size')" :span="12"> <n-form-item-gi :label="$t('preferences.general.scan_size')" :span="12">
@ -129,16 +115,16 @@ const onClose = () => {
:options="keyOptions" :options="keyOptions"
:render-label="({ label }) => $t(label)" /> :render-label="({ label }) => $t(label)" />
</n-form-item-gi> </n-form-item-gi>
<!-- <n-form-item-gi :label="$t('preferences.general.proxy')" :span="24">--> <n-form-item-gi :label="$t('preferences.general.proxy')" :span="24">
<!-- <n-space>--> <n-space>
<!-- <n-checkbox v-model:checked="prefStore.general.useSysProxy">--> <n-checkbox v-model:checked="prefStore.general.useSysProxy">
<!-- {{ $t('preferences.general.use_system_proxy') }}--> {{ $t('preferences.general.use_system_proxy') }}
<!-- </n-checkbox>--> </n-checkbox>
<!-- <n-checkbox v-model:checked="prefStore.general.useSysProxyHttp">--> <n-checkbox v-model:checked="prefStore.general.useSysProxyHttp">
<!-- {{ $t('preferences.general.use_system_proxy_http') }}--> {{ $t('preferences.general.use_system_proxy_http') }}
<!-- </n-checkbox>--> </n-checkbox>
<!-- </n-space>--> </n-space>
<!-- </n-form-item-gi>--> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.update')" :span="24"> <n-form-item-gi :label="$t('preferences.general.update')" :span="24">
<n-checkbox v-model:checked="prefStore.general.checkUpdate"> <n-checkbox v-model:checked="prefStore.general.checkUpdate">
{{ $t('preferences.general.auto_check_update') }} {{ $t('preferences.general.auto_check_update') }}
@ -151,27 +137,14 @@ const onClose = () => {
<n-tab-pane :tab="$t('preferences.editor.name')" display-directive="show" name="editor"> <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-form :disabled="loading" :model="prefStore.editor" :show-require-mark="false" label-placement="top">
<n-grid :x-gap="10"> <n-grid :x-gap="10">
<n-form-item-gi :span="24" required> <n-form-item-gi :label="$t('preferences.general.font')" :span="12" required>
<template #label>
{{ $t('preferences.general.font') }}
<n-tooltip trigger="hover">
<template #trigger>
<n-icon :component="Help" />
</template>
<div class="text-block">
{{ $t('preferences.font_tip') }}
</div>
</n-tooltip>
</template>
<n-select <n-select
v-model:value="prefStore.editor.fontFamily" v-model:value="prefStore.editor.font"
:options="prefStore.fontOption" :options="prefStore.fontOption"
:render-label="({ label, value }) => value || $t(label)" :render-label="({ label, value }) => value || $t(label)"
filterable filterable />
multiple
tag />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.font_size')" :span="24"> <n-form-item-gi :label="$t('preferences.general.font_size')" :span="12">
<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-gi> </n-form-item-gi>
<n-form-item-gi :show-feedback="false" :show-label="false" :span="24"> <n-form-item-gi :show-feedback="false" :show-label="false" :span="24">
@ -187,46 +160,6 @@ const onClose = () => {
</n-grid> </n-grid>
</n-form> </n-form>
</n-tab-pane> </n-tab-pane>
<n-tab-pane :tab="$t('preferences.cli.name')" display-directive="show" name="cli">
<n-form :disabled="loading" :model="prefStore.cli" :show-require-mark="false" label-placement="top">
<n-grid :x-gap="10">
<n-form-item-gi :span="24" required>
<template #label>
{{ $t('preferences.general.font') }}
<n-tooltip trigger="hover">
<template #trigger>
<n-icon :component="Help" />
</template>
<div class="text-block">
{{ $t('preferences.font_tip') }}
</div>
</n-tooltip>
</template>
<n-select
v-model:value="prefStore.cli.fontFamily"
:options="prefStore.fontOption"
:render-label="({ label, value }) => value || $t(label)"
filterable
multiple
tag />
</n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.font_size')" :span="24">
<n-input-number v-model:value="prefStore.cli.fontSize" :max="65535" :min="1" />
</n-form-item-gi>
<n-form-item-gi :label="$t('preferences.cli.cursor_style')" :span="24">
<n-radio-group v-model:value="prefStore.cli.cursorStyle" name="theme" size="medium">
<n-radio-button
v-for="opt in prefStore.cliCursorStyleOption"
:key="opt.value"
:value="opt.value">
{{ $t(opt.label) }}
</n-radio-button>
</n-radio-group>
</n-form-item-gi>
</n-grid>
</n-form>
</n-tab-pane>
</n-tabs> </n-tabs>
<!-- </n-spin> --> <!-- </n-spin> -->

View File

@ -1,9 +1,5 @@
<script setup> <script setup>
const props = defineProps({ const props = defineProps({
inverse: {
type: Boolean,
default: false,
},
strokeWidth: { strokeWidth: {
type: [Number, String], type: [Number, String],
default: 3, default: 3,
@ -14,9 +10,9 @@ const props = defineProps({
<template> <template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path <path
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M24 28.6292C26.5104 28.6292 28.5455 26.6004 28.5455 24.0979C28.5455 21.5954 26.5104 19.5667 24 19.5667C21.4897 19.5667 19.4546 21.5954 19.4546 24.0979C19.4546 26.6004 21.4897 28.6292 24 28.6292Z" d="M24 28.6292C26.5104 28.6292 28.5455 26.6004 28.5455 24.0979C28.5455 21.5954 26.5104 19.5667 24 19.5667C21.4897 19.5667 19.4546 21.5954 19.4546 24.0979C19.4546 26.6004 21.4897 28.6292 24 28.6292Z"
fill="none"
stroke="currentColor" stroke="currentColor"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path

View File

@ -46,15 +46,15 @@ const onSelectOptions = async (select) => {
<icon-button <icon-button
:button-class="['nav-pane-func-btn']" :button-class="['nav-pane-func-btn']"
:icon="AddLink" :icon="AddLink"
:stroke-width="3.5"
size="20" size="20"
:stroke-width="3.5"
t-tooltip="interface.new_conn" t-tooltip="interface.new_conn"
@click="dialogStore.openNewDialog()" /> @click="dialogStore.openNewDialog()" />
<icon-button <icon-button
:button-class="['nav-pane-func-btn']" :button-class="['nav-pane-func-btn']"
:icon="AddGroup" :icon="AddGroup"
:stroke-width="3.5"
size="20" size="20"
:stroke-width="3.5"
t-tooltip="interface.new_group" t-tooltip="interface.new_group"
@click="dialogStore.openNewGroupDialog()" /> @click="dialogStore.openNewGroupDialog()" />
<n-divider vertical /> <n-divider vertical />

View File

@ -152,8 +152,8 @@ const exThemeVars = computed(() => {
:options="preferencesOptions" :options="preferencesOptions"
:render-icon="({ icon }) => render.renderIcon(icon)" :render-icon="({ icon }) => render.renderIcon(icon)"
:render-label="({ label }) => render.renderLabel($t(label), { class: 'context-menu-item' })" :render-label="({ label }) => render.renderLabel($t(label), { class: 'context-menu-item' })"
content-class="nav-menu-button"
trigger="click" trigger="click"
content-class="nav-menu-button"
@select="onSelectPreferenceMenu"> @select="onSelectPreferenceMenu">
<icon-button :icon="Config" :size="iconSize" :stroke-width="3" /> <icon-button :icon="Config" :size="iconSize" :stroke-width="3" />
</n-dropdown> </n-dropdown>
@ -161,8 +161,8 @@ const exThemeVars = computed(() => {
v-if="prefStore.currentLanguage === 'zh'" v-if="prefStore.currentLanguage === 'zh'"
:icon="QRCode" :icon="QRCode"
:size="iconSize" :size="iconSize"
class="nav-menu-button"
t-tooltip="ribbon.wechat_official" t-tooltip="ribbon.wechat_official"
class="nav-menu-button"
@click="showWechat = true" /> @click="showWechat = true" />
<icon-button <icon-button
v-else v-else
@ -176,8 +176,8 @@ const exThemeVars = computed(() => {
<icon-button <icon-button
:icon="Github" :icon="Github"
:size="iconSize" :size="iconSize"
class="nav-menu-button"
t-tooltip="ribbon.github" t-tooltip="ribbon.github"
class="nav-menu-button"
@click="openGithub" /> @click="openGithub" />
</div> </div>

View File

@ -27,7 +27,6 @@
"preferences": { "preferences": {
"name": "Preferences", "name": "Preferences",
"restore_defaults": "Restore Defaults", "restore_defaults": "Restore Defaults",
"font_tip": "Supports multiple selection. You can manually input the font name if it is not list below.",
"general": { "general": {
"name": "General", "name": "General",
"theme": "Theme", "theme": "Theme",
@ -36,6 +35,7 @@
"theme_auto": "Auto", "theme_auto": "Auto",
"language": "Language", "language": "Language",
"system_lang": "Use System Language", "system_lang": "Use System Language",
"default": "Default",
"font": "Font", "font": "Font",
"font_size": "Font Size", "font_size": "Font Size",
"scan_size": "Default Size for SCAN Command", "scan_size": "Default Size for SCAN Command",
@ -54,13 +54,6 @@
"name": "Editor", "name": "Editor",
"show_linenum": "Show Line Numbers", "show_linenum": "Show Line Numbers",
"show_folding": "Enable Code Folding" "show_folding": "Enable Code Folding"
},
"cli": {
"name": "Command Line",
"cursor_style": "Cursor Style",
"cursor_style_block": "Block",
"cursor_style_underline": "Underline",
"cursor_style_bar": "Bar"
} }
}, },
"interface": { "interface": {
@ -275,7 +268,6 @@
}, },
"delete": { "delete": {
"success": "\"{key}\" has been deleted", "success": "\"{key}\" has been deleted",
"deleting": "Deleting",
"doing": "Deleting key({index}/{count})", "doing": "Deleting key({index}/{count})",
"completed": "Deletion process has been completed, {success} successed, {fail} failed" "completed": "Deletion process has been completed, {success} successed, {fail} failed"
}, },

View File

@ -31,6 +31,7 @@
"theme_auto": "Automático", "theme_auto": "Automático",
"language": "Idioma", "language": "Idioma",
"system_lang": "Usar Idioma do Sistema", "system_lang": "Usar Idioma do Sistema",
"default": "Padrão",
"font": "Fonte", "font": "Fonte",
"font_size": "Tamanho da Fonte", "font_size": "Tamanho da Fonte",
"scan_size": "Tamanho Padrão para Comando SCAN", "scan_size": "Tamanho Padrão para Comando SCAN",

View File

@ -27,7 +27,6 @@
"preferences": { "preferences": {
"name": "偏好设置", "name": "偏好设置",
"restore_defaults": "重置为默认", "restore_defaults": "重置为默认",
"font_tip": "支持多选,如列表没有已安装的字体,可自行手动输入",
"general": { "general": {
"name": "常规配置", "name": "常规配置",
"theme": "主题", "theme": "主题",
@ -36,6 +35,7 @@
"theme_auto": "自动", "theme_auto": "自动",
"language": "语言", "language": "语言",
"system_lang": "使用系统语言", "system_lang": "使用系统语言",
"default": "默认",
"font": "字体", "font": "字体",
"font_size": "字体尺寸", "font_size": "字体尺寸",
"scan_size": "SCAN命令默认数量", "scan_size": "SCAN命令默认数量",
@ -54,13 +54,6 @@
"name": "编辑器", "name": "编辑器",
"show_linenum": "显示行号", "show_linenum": "显示行号",
"show_folding": "启用代码折叠" "show_folding": "启用代码折叠"
},
"cli": {
"name": "命令行",
"cursor_style": "光标样式",
"cursor_style_block": "方块",
"cursor_style_underline": "下划线",
"cursor_style_bar": "竖线"
} }
}, },
"interface": { "interface": {
@ -275,7 +268,6 @@
}, },
"delete": { "delete": {
"success": "{key} 已被删除", "success": "{key} 已被删除",
"deleting": "正在删除",
"doing": "正在删除键({index}/{count})", "doing": "正在删除键({index}/{count})",
"completed": "已完成删除操作,成功{success}个,失败{fail}个" "completed": "已完成删除操作,成功{success}个,失败{fail}个"
}, },

View File

@ -36,7 +36,7 @@ import {
UpdateZSetValue, UpdateZSetValue,
} from 'wailsjs/go/services/browserService.js' } from 'wailsjs/go/services/browserService.js'
import useTabStore from 'stores/tab.js' import useTabStore from 'stores/tab.js'
import { nativeRedisKey } from '@/utils/key_convert.js' import { decodeRedisKey, nativeRedisKey } from '@/utils/key_convert.js'
import { BrowserTabType } from '@/consts/browser_tab_type.js' import { BrowserTabType } from '@/consts/browser_tab_type.js'
import { KeyViewType } from '@/consts/key_view_type.js' import { KeyViewType } from '@/consts/key_view_type.js'
import { ConnectionType } from '@/consts/connection_type.js' import { ConnectionType } from '@/consts/connection_type.js'
@ -44,7 +44,7 @@ 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' import { i18nGlobal } from '@/utils/i18n.js'
import { EventsEmit, EventsOn } from 'wailsjs/runtime/runtime.js' import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
import { RedisNodeItem } from '@/objects/redisNodeItem.js' import { RedisNodeItem } from '@/objects/redisNodeItem.js'
import { RedisServerState } from '@/objects/redisServerState.js' import { RedisServerState } from '@/objects/redisServerState.js'
import { RedisDatabaseItem } from '@/objects/redisDatabaseItem.js' import { RedisDatabaseItem } from '@/objects/redisDatabaseItem.js'
@ -1568,7 +1568,7 @@ const useBrowserStore = defineStore('browser', {
// $message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount })) // $message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
// } else { // } else {
// // some fail // // some fail
// $message.warning(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount })) // $message.warn(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
// } // }
}, },
@ -1617,16 +1617,31 @@ const useBrowserStore = defineStore('browser', {
* @return {Promise<void>} * @return {Promise<void>}
*/ */
async deleteKeys(server, db, keys) { async deleteKeys(server, db, keys) {
const msgRef = $message.loading(i18nGlobal.t('dialogue.delete.deleting'), { duration: 0, closable: true }) const msgRef = $message.loading('', { duration: 0, closable: true })
let deleted = [] let deleted = []
let failCount = 0 let failCount = 0
let canceled = false let canceled = false
const serialNo = Date.now().valueOf().toString() const serialNo = Date.now().valueOf().toString()
msgRef.onClose = () => { const eventName = 'deleting:' + serialNo
EventsEmit('delete:stop:' + serialNo) const cancelEvent = 'delete:stop:' + serialNo
}
try { try {
const { success, msg, data } = await DeleteKeys(server, db, keys, serialNo) let maxProgress = 0
EventsOn(eventName, ({ total, progress, processing }) => {
// update delete progress
if (progress > maxProgress) {
maxProgress = progress
}
const k = decodeRedisKey(processing)
msgRef.content = i18nGlobal.t('dialogue.delete.doing', {
key: k,
index: maxProgress,
count: total,
})
})
msgRef.onClose = () => {
EventsEmit(cancelEvent)
}
const { data, success, msg } = await DeleteKeys(server, db, keys, serialNo)
if (success) { if (success) {
canceled = get(data, 'canceled', false) canceled = get(data, 'canceled', false)
deleted = get(data, 'deleted', []) deleted = get(data, 'deleted', [])
@ -1636,6 +1651,7 @@ const useBrowserStore = defineStore('browser', {
} }
} finally { } finally {
msgRef.destroy() msgRef.destroy()
EventsOff(eventName)
// clear checked keys // clear checked keys
const tab = useTabStore() const tab = useTabStore()
tab.setCheckedKeys(server) tab.setCheckedKeys(server)
@ -1652,7 +1668,7 @@ const useBrowserStore = defineStore('browser', {
$message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount })) $message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
} else { } else {
// some fail // some fail
$message.warning(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount })) $message.warn(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
} }
// update ui // update ui
timeout(100).then(async () => { timeout(100).then(async () => {
@ -1687,7 +1703,9 @@ const useBrowserStore = defineStore('browser', {
let exported = 0 let exported = 0
let failCount = 0 let failCount = 0
let canceled = false let canceled = false
const cancelEventFn = EventsOn('exporting:' + path, ({ total, progress, processing }) => { const eventName = 'exporting:' + path
try {
EventsOn(eventName, ({ total, progress, processing }) => {
// update export progress // update export progress
msgRef.content = i18nGlobal.t('dialogue.export.exporting', { msgRef.content = i18nGlobal.t('dialogue.export.exporting', {
// key: decodeRedisKey(processing), // key: decodeRedisKey(processing),
@ -1698,7 +1716,6 @@ const useBrowserStore = defineStore('browser', {
msgRef.onClose = () => { msgRef.onClose = () => {
EventsEmit('export:stop:' + path) EventsEmit('export:stop:' + path)
} }
try {
const { data, success, msg } = await ExportKey(server, db, keys, path, expire) const { data, success, msg } = await ExportKey(server, db, keys, path, expire)
if (success) { if (success) {
canceled = get(data, 'canceled', false) canceled = get(data, 'canceled', false)
@ -1709,7 +1726,7 @@ const useBrowserStore = defineStore('browser', {
} }
} finally { } finally {
msgRef.destroy() msgRef.destroy()
cancelEventFn() EventsOff(eventName)
} }
if (canceled) { if (canceled) {
$message.info(i18nGlobal.t('dialogue.handle_cancel')) $message.info(i18nGlobal.t('dialogue.handle_cancel'))
@ -1723,12 +1740,7 @@ const useBrowserStore = defineStore('browser', {
$message.error(i18nGlobal.t('dialogue.export.export_completed', { success: exported, fail: failCount })) $message.error(i18nGlobal.t('dialogue.export.export_completed', { success: exported, fail: failCount }))
} else { } else {
// some fail // some fail
$message.warning( $message.warn(i18nGlobal.t('dialogue.export.export_completed', { success: exported, fail: failCount }))
i18nGlobal.t('dialogue.export.export_completed', {
success: exported,
fail: failCount,
}),
)
} }
}, },
@ -1747,7 +1759,9 @@ const useBrowserStore = defineStore('browser', {
let imported = 0 let imported = 0
let ignored = 0 let ignored = 0
let canceled = false let canceled = false
const cancelEventFn = EventsOn('importing:' + path, ({ imported = 0, ignored = 0 }) => { const eventName = 'importing:' + path
try {
EventsOn(eventName, ({ imported = 0, ignored = 0 }) => {
// update export progress // update export progress
msgRef.content = i18nGlobal.t('dialogue.import.importing', { msgRef.content = i18nGlobal.t('dialogue.import.importing', {
// key: decodeRedisKey(processing), // key: decodeRedisKey(processing),
@ -1758,7 +1772,6 @@ const useBrowserStore = defineStore('browser', {
msgRef.onClose = () => { msgRef.onClose = () => {
EventsEmit('import:stop:' + path) EventsEmit('import:stop:' + path)
} }
try {
const { data, success, msg } = await ImportCSV(server, db, path, conflict, ttl) const { data, success, msg } = await ImportCSV(server, db, path, conflict, ttl)
if (success) { if (success) {
canceled = get(data, 'canceled', false) canceled = get(data, 'canceled', false)
@ -1768,8 +1781,8 @@ const useBrowserStore = defineStore('browser', {
$message.error(msg) $message.error(msg)
} }
} finally { } finally {
cancelEventFn()
msgRef.destroy() msgRef.destroy()
EventsOff(eventName)
} }
if (canceled) { if (canceled) {
$message.info(i18nGlobal.t('dialogue.handle_cancel')) $message.info(i18nGlobal.t('dialogue.handle_cancel'))

View File

@ -105,7 +105,7 @@ const useConnectionStore = defineStore('connections', {
defaultFilter: item.defaultFilter, defaultFilter: item.defaultFilter,
keySeparator: item.keySeparator, keySeparator: item.keySeparator,
markColor: item.markColor, markColor: item.markColor,
refreshInterval: item.refreshInterval, refreshInterval: conn.refreshInterval,
} }
} }
conns.push({ conns.push({

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { lang } from '@/langs/index.js' import { lang } from '@/langs/index.js'
import { cloneDeep, get, isEmpty, join, map, pick, set, split } from 'lodash' import { cloneDeep, find, get, isEmpty, map, pick, set, split } from 'lodash'
import { import {
CheckForUpdate, CheckForUpdate,
GetFontList, GetFontList,
@ -43,7 +43,6 @@ const usePreferencesStore = defineStore('preferences', {
theme: 'auto', theme: 'auto',
language: 'auto', language: 'auto',
font: '', font: '',
fontFamily: [],
fontSize: 14, fontSize: 14,
scanSize: 3000, scanSize: 3000,
keyIconStyle: 0, keyIconStyle: 0,
@ -54,16 +53,10 @@ const usePreferencesStore = defineStore('preferences', {
}, },
editor: { editor: {
font: '', font: '',
fontFamily: [],
fontSize: 14, fontSize: 14,
showLineNum: true, showLineNum: true,
showFolding: true, showFolding: true,
}, },
cli: {
fontFamily: [],
fontSize: 14,
cursorStyle: 'block',
},
lastPref: {}, lastPref: {},
fontList: [], fontList: [],
}), }),
@ -110,11 +103,17 @@ const usePreferencesStore = defineStore('preferences', {
* @returns {{path: string, label: string, value: string}[]} * @returns {{path: string, label: string, value: string}[]}
*/ */
fontOption() { fontOption() {
return map(this.fontList, (font) => ({ const option = map(this.fontList, (font) => ({
value: font.name, value: font.name,
label: font.name, label: font.name,
path: font.path, path: font.path,
})) }))
option.splice(0, 0, {
value: '',
label: 'preferences.general.default',
path: '',
})
return option
}, },
/** /**
@ -125,21 +124,12 @@ const usePreferencesStore = defineStore('preferences', {
const fontStyle = { const fontStyle = {
fontSize: this.general.fontSize + 'px', fontSize: this.general.fontSize + 'px',
} }
if (!isEmpty(this.general.fontFamily)) { if (!isEmpty(this.general.font) && this.general.font !== 'none') {
fontStyle['fontFamily'] = join( const font = find(this.fontList, { name: this.general.font })
map(this.general.fontFamily, (f) => `"${f}"`), if (font != null) {
',', fontStyle['fontFamily'] = `${font.name}`
) }
} }
// compatible with old preferences
// if (isEmpty(fontStyle['fontFamily'])) {
// if (!isEmpty(this.general.font) && this.general.font !== 'none') {
// const font = find(this.fontList, { name: this.general.font })
// if (font != null) {
// fontStyle['fontFamily'] = `${font.name}`
// }
// }
// }
return fontStyle return fontStyle
}, },
@ -151,64 +141,16 @@ const usePreferencesStore = defineStore('preferences', {
const fontStyle = { const fontStyle = {
fontSize: (this.editor.fontSize || 14) + 'px', fontSize: (this.editor.fontSize || 14) + 'px',
} }
if (!isEmpty(this.editor.fontFamily)) { if (!isEmpty(this.editor.font) && this.editor.font !== 'none') {
fontStyle['fontFamily'] = join( const font = find(this.fontList, { name: this.editor.font })
map(this.editor.fontFamily, (f) => `"${f}"`), if (font != null) {
',', fontStyle['fontFamily'] = `${font.name}`
)
} }
// compatible with old preferences
// if (isEmpty(fontStyle['fontFamily'])) {
// if (!isEmpty(this.editor.font) && this.editor.font !== 'none') {
// const font = find(this.fontList, { name: this.editor.font })
// if (font != null) {
// fontStyle['fontFamily'] = `${font.name}`
// }
// }
// }
if (isEmpty(fontStyle['fontFamily'])) {
fontStyle['fontFamily'] = ['monaco']
} }
fontStyle['fontFamily'] = fontStyle['fontFamily'] || 'monaco'
return fontStyle return fontStyle
}, },
/**
* current cli font
* @return {{fontSize: string, fontFamily?: string}}
*/
cliFont() {
const fontStyle = {
fontSize: this.cli.fontSize || 14,
}
if (!isEmpty(this.cli.fontFamily)) {
fontStyle['fontFamily'] = join(
map(this.cli.fontFamily, (f) => `"${f}"`),
',',
)
}
if (isEmpty(fontStyle['fontFamily'])) {
fontStyle['fontFamily'] = ['Courier New']
}
return fontStyle
},
cliCursorStyleOption() {
return [
{
value: 'block',
label: 'preferences.cli.cursor_style_block',
},
{
value: 'underline',
label: 'preferences.cli.cursor_style_underline',
},
{
value: 'bar',
label: 'preferences.cli.cursor_style_bar',
},
]
},
/** /**
* get current language setting * get current language setting
* @return {string} * @return {string}
@ -306,7 +248,7 @@ const usePreferencesStore = defineStore('preferences', {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
async savePreferences() { async savePreferences() {
const pf = pick(this, ['behavior', 'general', 'editor', 'cli']) const pf = pick(this, ['behavior', 'general', 'editor'])
const { success, msg } = await SetPreferences(pf) const { success, msg } = await SetPreferences(pf)
return success === true return success === true
}, },