feat: add export capability for selected keys
This commit is contained in:
parent
b06217adc0
commit
bce4e2323e
|
@ -2,11 +2,15 @@ package services
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"net/url"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -1936,6 +1940,77 @@ func (b *browserService) DeleteOneKey(server string, db int, k any) (resp types.
|
|||
return
|
||||
}
|
||||
|
||||
// ExportKey export keys
|
||||
func (b *browserService) ExportKey(server string, db int, ks []any, path string) (resp types.JSResp) {
|
||||
// connect a new connection to export keys
|
||||
conf := Connection().getConnection(server)
|
||||
if conf == nil {
|
||||
resp.Msg = fmt.Sprintf("no connection profile named: %s", server)
|
||||
return
|
||||
}
|
||||
var client redis.UniversalClient
|
||||
var err error
|
||||
var connConfig = conf.ConnectionConfig
|
||||
connConfig.LastDB = db
|
||||
if client, err = Connection().createRedisClient(connConfig); err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
// TODO: add cancel handle
|
||||
ctx, cancelFunc := context.WithCancel(b.ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
writer := csv.NewWriter(file)
|
||||
defer writer.Flush()
|
||||
|
||||
runtime.EventsOnce(ctx, "export:stop:"+path, func(data ...any) {
|
||||
cancelFunc()
|
||||
})
|
||||
processEvent := "exporting:" + path
|
||||
total := len(ks)
|
||||
var exported, failed int64
|
||||
var canceled bool
|
||||
for i, k := range ks {
|
||||
param := map[string]any{
|
||||
"total": total,
|
||||
"progress": i + 1,
|
||||
"processing": k,
|
||||
}
|
||||
runtime.EventsEmit(b.ctx, processEvent, param)
|
||||
|
||||
key := strutil.DecodeRedisKey(k)
|
||||
content, dumpErr := client.Dump(ctx, key).Bytes()
|
||||
if errors.Is(dumpErr, context.Canceled) {
|
||||
canceled = true
|
||||
break
|
||||
}
|
||||
if err = writer.Write([]string{hex.EncodeToString([]byte(key)), hex.EncodeToString(content)}); err != nil {
|
||||
failed += 1
|
||||
} else {
|
||||
exported += 1
|
||||
}
|
||||
}
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = struct {
|
||||
Canceled bool `json:"canceled"`
|
||||
Exported int64 `json:"exported"`
|
||||
Failed int64 `json:"failed"`
|
||||
}{
|
||||
Canceled: canceled,
|
||||
Exported: exported,
|
||||
Failed: failed,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FlushDB flush database
|
||||
func (b *browserService) FlushDB(connName string, db int, async bool) (resp types.JSResp) {
|
||||
item, err := b.getRedisClient(connName, db)
|
||||
|
|
|
@ -127,6 +127,9 @@ func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.O
|
|||
WriteTimeout: time.Duration(config.ExecTimeout) * time.Second,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
if config.LastDB > 0 {
|
||||
option.DB = config.LastDB
|
||||
}
|
||||
if sshClient != nil {
|
||||
option.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return sshClient.Dial(network, addr)
|
||||
|
|
|
@ -3,11 +3,11 @@ package services
|
|||
import (
|
||||
"context"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
"tinyrdm/backend/consts"
|
||||
"tinyrdm/backend/types"
|
||||
sliceutil "tinyrdm/backend/utils/slice"
|
||||
)
|
||||
|
||||
type systemService struct {
|
||||
|
@ -50,7 +50,30 @@ func (s *systemService) SelectFile(title string) (resp types.JSResp) {
|
|||
ShowHiddenFiles: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
resp.Success = true
|
||||
resp.Data = map[string]any{
|
||||
"path": filepath,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SaveFile open file dialog to save a file
|
||||
func (s *systemService) SaveFile(title string, defaultName string, extensions []string) (resp types.JSResp) {
|
||||
filters := sliceutil.Map(extensions, func(i int) runtime.FileFilter {
|
||||
return runtime.FileFilter{
|
||||
Pattern: "*." + extensions[i],
|
||||
}
|
||||
})
|
||||
filepath, err := runtime.SaveFileDialog(s.ctx, runtime.SaveDialogOptions{
|
||||
Title: title,
|
||||
ShowHiddenFiles: true,
|
||||
DefaultFilename: defaultName,
|
||||
Filters: filters,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import { WindowSetDarkTheme, WindowSetLightTheme } from 'wailsjs/runtime/runtime
|
|||
import { darkThemeOverrides, themeOverrides } from '@/utils/theme.js'
|
||||
import AboutDialog from '@/components/dialogs/AboutDialog.vue'
|
||||
import FlushDbDialog from '@/components/dialogs/FlushDbDialog.vue'
|
||||
import ExportKeyDialog from '@/components/dialogs/ExportKeyDialog.vue'
|
||||
|
||||
const prefStore = usePreferencesStore()
|
||||
const connectionStore = useConnectionStore()
|
||||
|
@ -67,6 +68,7 @@ watch(
|
|||
<add-fields-dialog />
|
||||
<rename-key-dialog />
|
||||
<delete-key-dialog />
|
||||
<export-key-dialog />
|
||||
<flush-db-dialog />
|
||||
<set-ttl-dialog />
|
||||
<preferences-dialog />
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<script setup>
|
||||
import { SaveFile } from 'wailsjs/go/services/systemService.js'
|
||||
import { get } from 'lodash'
|
||||
|
||||
const props = defineProps({
|
||||
value: String,
|
||||
placeholder: String,
|
||||
disabled: Boolean,
|
||||
defaultPath: String,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:value'])
|
||||
|
||||
const onClear = () => {
|
||||
emit('update:value', '')
|
||||
}
|
||||
|
||||
const handleSaveFile = async () => {
|
||||
const { success, data } = await SaveFile(null, props.defaultPath, ['csv'])
|
||||
if (success) {
|
||||
const path = get(data, 'path', '')
|
||||
emit('update:value', path)
|
||||
} else {
|
||||
emit('update:value', '')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-input-group>
|
||||
<n-input
|
||||
v-model:value="props.value"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="placeholder"
|
||||
clearable
|
||||
@clear="onClear" />
|
||||
<n-button :disabled="props.disabled" :focusable="false" @click="handleSaveFile">...</n-button>
|
||||
</n-input-group>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -99,10 +99,10 @@ const onClose = () => {
|
|||
<n-form :model="deleteForm" :show-require-mark="false" label-placement="top">
|
||||
<n-grid :x-gap="10">
|
||||
<n-form-item-gi :label="$t('dialogue.key.server')" :span="12">
|
||||
<n-input :value="deleteForm.server" readonly />
|
||||
<n-input :autofocus="false" :value="deleteForm.server" readonly />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="$t('dialogue.key.db_index')" :span="12">
|
||||
<n-input :value="deleteForm.db.toString()" readonly />
|
||||
<n-input :autofocus="false" :value="deleteForm.db.toString()" readonly />
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
<n-form-item
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<script setup>
|
||||
import { computed, reactive, ref, watchEffect } from 'vue'
|
||||
import useDialog from 'stores/dialog'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import FileSaveInput from '@/components/common/FileSaveInput.vue'
|
||||
import { isEmpty, map, size } from 'lodash'
|
||||
import { decodeRedisKey } from '@/utils/key_convert.js'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const exportKeyForm = reactive({
|
||||
server: '',
|
||||
db: 0,
|
||||
keys: [],
|
||||
file: '',
|
||||
})
|
||||
|
||||
const dialogStore = useDialog()
|
||||
const browserStore = useBrowserStore()
|
||||
const loading = ref(false)
|
||||
const deleting = ref(false)
|
||||
watchEffect(() => {
|
||||
if (dialogStore.exportKeyDialogVisible) {
|
||||
const { server, db, keys } = dialogStore.exportKeyParam
|
||||
exportKeyForm.server = server
|
||||
exportKeyForm.db = db
|
||||
exportKeyForm.keys = keys
|
||||
// exportKeyForm.async = true
|
||||
deleting.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const keyLines = computed(() => {
|
||||
return map(exportKeyForm.keys, (k) => decodeRedisKey(k))
|
||||
})
|
||||
|
||||
const exportEnable = computed(() => {
|
||||
return !isEmpty(exportKeyForm.keys) && !isEmpty(exportKeyForm.file)
|
||||
})
|
||||
|
||||
const i18n = useI18n()
|
||||
const onConfirmDelete = async () => {
|
||||
try {
|
||||
deleting.value = true
|
||||
const { server, db, keys, file } = exportKeyForm
|
||||
browserStore.exportKeys(server, db, keys, file).catch((e) => {})
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
return
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
dialogStore.closeExportKeyDialog()
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
dialogStore.closeExportKeyDialog()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="dialogStore.exportKeyDialogVisible"
|
||||
:closable="false"
|
||||
:close-on-esc="false"
|
||||
:mask-closable="false"
|
||||
:show-icon="false"
|
||||
:title="$t('dialogue.export.name')"
|
||||
preset="dialog"
|
||||
transform-origin="center">
|
||||
<n-spin :show="loading">
|
||||
<n-form :model="exportKeyForm" :show-require-mark="false" label-placement="top">
|
||||
<n-grid :x-gap="10">
|
||||
<n-form-item-gi :label="$t('dialogue.key.server')" :span="12">
|
||||
<n-input :autofocus="false" :value="exportKeyForm.server" readonly />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="$t('dialogue.key.db_index')" :span="12">
|
||||
<n-input :autofocus="false" :value="exportKeyForm.db.toString()" readonly />
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
<n-form-item :label="$t('dialogue.export.save_file')" required>
|
||||
<file-save-input
|
||||
v-model:value="exportKeyForm.file"
|
||||
:default-path="`export_${dayjs().format('YYYYMMDDHHmmss')}.csv`"
|
||||
:placeholder="$t('dialogue.export.save_file_tip')" />
|
||||
</n-form-item>
|
||||
<n-card
|
||||
:title="$t('dialogue.key.affected_key') + `(${size(exportKeyForm.keys)})`"
|
||||
embedded
|
||||
size="small">
|
||||
<n-log :line-height="1.5" :lines="keyLines" :rows="10" style="user-select: text; cursor: text" />
|
||||
</n-card>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
|
||||
<template #action>
|
||||
<div class="flex-item n-dialog__action">
|
||||
<n-button :disabled="loading" :focusable="false" @click="onClose">{{ $t('common.cancel') }}</n-button>
|
||||
<n-button
|
||||
:disabled="!exportEnable"
|
||||
:focusable="false"
|
||||
:loading="loading"
|
||||
type="error"
|
||||
@click="onConfirmDelete">
|
||||
{{ $t('dialogue.export.export') }}
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,45 @@
|
|||
<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">
|
||||
<mask
|
||||
id="icon-122098f7f10b972"
|
||||
height="48"
|
||||
maskUnits="userSpaceOnUse"
|
||||
style="mask-type: alpha"
|
||||
width="48"
|
||||
x="0"
|
||||
y="0">
|
||||
<path d="M48 0H0V48H48V0Z" fill="currentColor" />
|
||||
</mask>
|
||||
<g mask="url(#icon-122098f7f10b972)">
|
||||
<path
|
||||
:stroke-width="props.strokeWidth"
|
||||
d="M6 24.0083V42H42V24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
:stroke-width="props.strokeWidth"
|
||||
d="M33 15L24 6L15 15"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
:stroke-width="props.strokeWidth"
|
||||
d="M23.9917 32V6"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,33 @@
|
|||
<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
|
||||
:stroke-width="props.strokeWidth"
|
||||
d="M6 24.0083V42H42V24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
:stroke-width="props.strokeWidth"
|
||||
d="M33 23L24 32L15 23"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
:stroke-width="props.strokeWidth"
|
||||
d="M23.9917 6V32"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -23,6 +23,7 @@ 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'
|
||||
import Export from '@/components/icons/Export.vue'
|
||||
|
||||
const props = defineProps({
|
||||
server: String,
|
||||
|
@ -146,6 +147,10 @@ const onDeleteChecked = () => {
|
|||
browserTreeRef.value?.deleteCheckedItems()
|
||||
}
|
||||
|
||||
const onExportChecked = () => {
|
||||
browserTreeRef.value?.exportCheckedItems()
|
||||
}
|
||||
|
||||
const onFlush = () => {
|
||||
dialogStore.openFlushDBDialog(props.server, props.db)
|
||||
}
|
||||
|
@ -329,6 +334,14 @@ onMounted(() => onReload())
|
|||
|
||||
<!-- 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="Export"
|
||||
:stroke-width="3.5"
|
||||
size="20"
|
||||
t-tooltip="interface.export_checked"
|
||||
@click="onExportChecked" />
|
||||
<icon-button
|
||||
:button-class="['nav-pane-func-btn']"
|
||||
:disabled="checkedCount <= 0"
|
||||
|
|
|
@ -581,6 +581,13 @@ defineExpose({
|
|||
dialogStore.openDeleteKeyDialog(props.server, props.db, redisKeys)
|
||||
}
|
||||
},
|
||||
exportCheckedItems: () => {
|
||||
const checkedKeys = tabStore.currentCheckedKeys
|
||||
const redisKeys = map(checkedKeys, 'redisKey')
|
||||
if (!isEmpty(redisKeys)) {
|
||||
dialogStore.openExportKeyDialog(props.server, props.db, redisKeys)
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
"check_mode": "Check Mode",
|
||||
"quit_check_mode": "Quit Check Mode",
|
||||
"delete_checked": "Delete Checked Items",
|
||||
"export_checked": "Export Checked Items",
|
||||
"copy_value": "Copy Value",
|
||||
"edit_value": "Edit Value",
|
||||
"save_update": "Save Update",
|
||||
|
@ -146,6 +147,7 @@
|
|||
"delete_completed": "Deletion process has been completed, {success} successed, {fail} failed",
|
||||
"rename_binary_key_fail": "Rename binary key name is unsupported",
|
||||
"handle_succ": "Success!",
|
||||
"handle_cancel": "The operation has been canceled.",
|
||||
"reload_succ": "Reloaded!",
|
||||
"field_required": "This item should not be blank",
|
||||
"spec_field_required": "\"{key}\" should not be blank",
|
||||
|
@ -269,6 +271,14 @@
|
|||
"filter_pattern": "Pattern",
|
||||
"filter_pattern_tip": "* : Matches zero or more characters. For example, 'key*' matches all keys starting with 'key'.\n? : Matches a single character. For example, 'key?' matches 'key1', 'key2'.\n[] : Matches a single character within the specified range. For example, 'key[1-3]' matches keys like 'key1', 'key2', 'key3'.\n\\ : Escape character. To match *, ?, [, or ], use the backslash '\\' for escaping."
|
||||
},
|
||||
"export": {
|
||||
"name": "Export Keys",
|
||||
"export": "Export",
|
||||
"save_file": "Export Path",
|
||||
"save_file_tip": "Select the export file save path",
|
||||
"exporting": "Exporting key: {key} ({index}/{count})",
|
||||
"export_completed": "Export process has been completed, {success} successed, {fail} failed"
|
||||
},
|
||||
"ttl": {
|
||||
"title": "Set Key TTL"
|
||||
},
|
||||
|
|
|
@ -78,7 +78,8 @@
|
|||
"flush_db": "清空数据库",
|
||||
"check_mode": "勾选模式",
|
||||
"quit_check_mode": "退出勾选模式",
|
||||
"delete_checked": "删除勾选项",
|
||||
"delete_checked": "删除所选项",
|
||||
"export_checked": "导出所选项",
|
||||
"copy_value": "复制值",
|
||||
"edit_value": "修改值",
|
||||
"save_update": "保存修改",
|
||||
|
@ -146,6 +147,7 @@
|
|||
"delete_completed": "已完成删除操作,成功{success}个,失败{fail}个",
|
||||
"rename_binary_key_fail": "不支持重命名二进制键名",
|
||||
"handle_succ": "操作成功",
|
||||
"handle_cancel": "操作已取消",
|
||||
"reload_succ": "已重新载入",
|
||||
"field_required": "此项不能为空",
|
||||
"spec_field_required": "{key} 不能为空",
|
||||
|
@ -268,6 +270,14 @@
|
|||
"filter_pattern": "过滤表达式",
|
||||
"filter_pattern_tip": "*:匹配零个或多个字符。例如:\"key*\"匹配到以\"key\"开头的所有键\n?:匹配单个字符。例如:\"key?\"匹配\"key1\"、\"key2\"\n[ ]:匹配指定范围内的单个字符。例如:\"key[1-3]\"可以匹配类似于 \"key1\"、\"key2\"、\"key3\" 的键\n\\:转义字符。如果想要匹配 *、?、[、或],需要使用反斜杠\"\\\"进行转义"
|
||||
},
|
||||
"export": {
|
||||
"name": "导出键",
|
||||
"export": "确认导出",
|
||||
"save_file": "导出路径",
|
||||
"save_file_tip": "选择保存文件路径",
|
||||
"exporting": "正在导出键:{key} ({index}/{count})",
|
||||
"export_completed": "已完成导出操作,成功{success}个,失败{fail}个"
|
||||
},
|
||||
"ttl": {
|
||||
"title": "设置键存活时间"
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
ConvertValue,
|
||||
DeleteKey,
|
||||
DeleteOneKey,
|
||||
ExportKey,
|
||||
FlushDB,
|
||||
GetCmdHistory,
|
||||
GetKeyDetail,
|
||||
|
@ -56,6 +57,7 @@ import useConnectionStore from 'stores/connections.js'
|
|||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import { isRedisGlob } from '@/utils/glob_pattern.js'
|
||||
import { i18nGlobal } from '@/utils/i18n.js'
|
||||
import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
|
||||
|
||||
const useBrowserStore = defineStore('browser', {
|
||||
/**
|
||||
|
@ -2012,6 +2014,60 @@ const useBrowserStore = defineStore('browser', {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* export multiple keys
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string[]|number[][]} keys
|
||||
* @param {string} path
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async exportKeys(server, db, keys, path) {
|
||||
const delMsgRef = $message.loading('', { duration: 0, closable: true })
|
||||
let exported = 0
|
||||
let failCount = 0
|
||||
let canceled = false
|
||||
const eventName = 'exporting:' + path
|
||||
try {
|
||||
EventsOn(eventName, ({ total, progress, processing }) => {
|
||||
// update export progress
|
||||
delMsgRef.content = i18nGlobal.t('dialogue.export.exporting', {
|
||||
key: decodeRedisKey(processing),
|
||||
index: progress,
|
||||
count: total,
|
||||
})
|
||||
})
|
||||
delMsgRef.onClose = () => {
|
||||
EventsEmit('export:stop:' + path)
|
||||
}
|
||||
const { data, success, msg } = await ExportKey(server, db, keys, path)
|
||||
if (success) {
|
||||
canceled = get(data, 'canceled', false)
|
||||
exported = get(data, 'exported', 0)
|
||||
failCount = get(data, 'failed', 0)
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
} finally {
|
||||
delMsgRef.destroy()
|
||||
EventsOff(eventName)
|
||||
}
|
||||
if (canceled) {
|
||||
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
|
||||
} else if (failCount <= 0) {
|
||||
// no fail
|
||||
$message.success(
|
||||
i18nGlobal.t('dialogue.export.export_completed', { success: exported, fail: failCount }),
|
||||
)
|
||||
} else if (failCount >= exported) {
|
||||
// all fail
|
||||
$message.error(i18nGlobal.t('dialogue.export.export_completed', { success: exported, fail: failCount }))
|
||||
} else {
|
||||
// some fail
|
||||
$message.warn(i18nGlobal.t('dialogue.export.export_completed', { success: exported, fail: failCount }))
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* flush database
|
||||
* @param connName
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from "pinia";
|
||||
import useConnectionStore from "./connections.js";
|
||||
import { defineStore } from 'pinia'
|
||||
import useConnectionStore from './connections.js'
|
||||
|
||||
/**
|
||||
* connection dialog type
|
||||
|
@ -63,6 +63,13 @@ const useDialogStore = defineStore('dialog', {
|
|||
},
|
||||
deleteKeyDialogVisible: false,
|
||||
|
||||
exportKeyParam: {
|
||||
server: '',
|
||||
db: 0,
|
||||
keys: [],
|
||||
},
|
||||
exportKeyDialogVisible: false,
|
||||
|
||||
flushDBParam: {
|
||||
server: '',
|
||||
db: 0,
|
||||
|
@ -176,6 +183,22 @@ const useDialogStore = defineStore('dialog', {
|
|||
this.deleteKeyDialogVisible = false
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string|string[]} keys
|
||||
*/
|
||||
openExportKeyDialog(server, db, keys) {
|
||||
this.exportKeyParam.server = server
|
||||
this.exportKeyParam.db = db
|
||||
this.exportKeyParam.keys = keys
|
||||
this.exportKeyDialogVisible = true
|
||||
},
|
||||
closeExportKeyDialog() {
|
||||
this.exportKeyDialogVisible = false
|
||||
},
|
||||
|
||||
openFlushDBDialog(server, db) {
|
||||
this.flushDBParam.server = server
|
||||
this.flushDBParam.db = db
|
||||
|
|
Loading…
Reference in New Issue