Add common confirm dialog function

Add connection group handle logic
Refactor local connection profile struct
This commit is contained in:
tiny-craft 2023-07-02 16:52:07 +08:00
parent 8e3adbf0c3
commit f6a4ab8de0
16 changed files with 506 additions and 218 deletions

View File

@ -92,7 +92,7 @@ func (c *connectionService) GetConnection(name string) (resp types.JSResp) {
}
// SaveConnection save connection config to local profile
func (c *connectionService) SaveConnection(name string, param types.Connection) (resp types.JSResp) {
func (c *connectionService) SaveConnection(name string, param types.ConnectionConfig) (resp types.JSResp) {
var err error
if len(name) > 0 {
// update connection
@ -119,6 +119,39 @@ func (c *connectionService) RemoveConnection(name string) (resp types.JSResp) {
return
}
// CreateGroup create new group
func (c *connectionService) CreateGroup(name string) (resp types.JSResp) {
err := c.conns.CreateGroup(name)
if err != nil {
resp.Msg = err.Error()
return
}
resp.Success = true
return
}
// RenameGroup rename group
func (c *connectionService) RenameGroup(name, newName string) (resp types.JSResp) {
err := c.conns.RenameGroup(name, newName)
if err != nil {
resp.Msg = err.Error()
return
}
resp.Success = true
return
}
// RemoveGroup remove group by name
func (c *connectionService) RemoveGroup(name string, includeConn bool) (resp types.JSResp) {
err := c.conns.RemoveGroup(name, includeConn)
if err != nil {
resp.Msg = err.Error()
return
}
resp.Success = true
return
}
// OpenConnection open redis server connection
func (c *connectionService) OpenConnection(name string) (resp types.JSResp) {
rdb, ctx, err := c.getRedisClient(name, 0)
@ -195,18 +228,9 @@ func (c *connectionService) getRedisClient(connName string, db int) (*redis.Clie
if ok {
rdb, ctx = item.rdb, item.ctx
} else {
connGroups := c.conns.GetConnections()
var selConn *types.Connection
for _, connGroup := range connGroups {
for _, conn := range connGroup.Connections {
if conn.Name == connName {
selConn = &conn
break
}
}
}
selConn := c.conns.GetConnection(connName)
if selConn == nil {
return nil, nil, errors.New("no match connection connName")
return nil, nil, fmt.Errorf("no match connection \"%s\"", connName)
}
rdb = redis.NewClient(&redis.Options{

View File

@ -5,7 +5,6 @@ import (
"gopkg.in/yaml.v3"
"sync"
"tinyrdm/backend/types"
sliceutil "tinyrdm/backend/utils/slice"
)
type ConnectionsStorage struct {
@ -19,17 +18,12 @@ func NewConnections() *ConnectionsStorage {
}
}
func (c *ConnectionsStorage) defaultConnections() []types.ConnectionGroup {
return []types.ConnectionGroup{
{
GroupName: "",
Connections: []types.Connection{},
},
}
func (c *ConnectionsStorage) defaultConnections() types.Connections {
return types.Connections{}
}
func (c *ConnectionsStorage) defaultConnectionItem() types.Connection {
return types.Connection{
func (c *ConnectionsStorage) defaultConnectionItem() types.ConnectionConfig {
return types.ConnectionConfig{
Name: "",
Addr: "127.0.0.1",
Port: 6379,
@ -43,7 +37,7 @@ func (c *ConnectionsStorage) defaultConnectionItem() types.Connection {
}
}
func (c *ConnectionsStorage) getConnections() (ret []types.ConnectionGroup) {
func (c *ConnectionsStorage) getConnections() (ret types.Connections) {
b, err := c.storage.Load()
if err != nil {
ret = c.defaultConnections()
@ -66,15 +60,19 @@ func (c *ConnectionsStorage) getConnections() (ret []types.ConnectionGroup) {
}
// GetConnections get all store connections from local
func (c *ConnectionsStorage) GetConnections() (ret []types.ConnectionGroup) {
func (c *ConnectionsStorage) GetConnections() (ret types.Connections) {
return c.getConnections()
}
// GetConnectionsFlat get all store connections from local flat(exclude group level)
func (c *ConnectionsStorage) GetConnectionsFlat() (ret []types.Connection) {
func (c *ConnectionsStorage) GetConnectionsFlat() (ret types.Connections) {
conns := c.getConnections()
for _, group := range conns {
ret = append(ret, group.Connections...)
for _, conn := range conns {
if conn.Type == "group" {
ret = append(ret, conn.Connections...)
} else {
ret = append(ret, conn)
}
}
return
}
@ -82,18 +80,40 @@ func (c *ConnectionsStorage) GetConnectionsFlat() (ret []types.Connection) {
// GetConnection get connection by name
func (c *ConnectionsStorage) GetConnection(name string) *types.Connection {
conns := c.getConnections()
for _, group := range conns {
for _, conn := range group.Connections {
if conn.Name == name {
conn.Group = group.GroupName
return &conn
var findConn func(string, string, types.Connections) *types.Connection
findConn = func(name, groupName string, conns types.Connections) *types.Connection {
for i, conn := range conns {
if conn.Type != "group" {
if conn.Name == name {
conns[i].Group = groupName
return &conns[i]
}
} else {
if ret := findConn(name, conn.Name, conn.Connections); ret != nil {
return ret
}
}
}
return nil
}
return findConn(name, "", conns)
}
// GetGroup get connection group by name
func (c *ConnectionsStorage) GetGroup(name string) *types.Connection {
conns := c.getConnections()
for i, conn := range conns {
if conn.Type == "group" && conn.Name == name {
return &conns[i]
}
}
return nil
}
func (c *ConnectionsStorage) saveConnections(conns []types.ConnectionGroup) error {
func (c *ConnectionsStorage) saveConnections(conns types.Connections) error {
b, err := yaml.Marshal(&conns)
if err != nil {
return err
@ -105,7 +125,7 @@ func (c *ConnectionsStorage) saveConnections(conns []types.ConnectionGroup) erro
}
// CreateConnection create new connection
func (c *ConnectionsStorage) CreateConnection(param types.Connection) error {
func (c *ConnectionsStorage) CreateConnection(param types.ConnectionConfig) error {
c.mutex.Lock()
defer c.mutex.Unlock()
@ -115,72 +135,75 @@ func (c *ConnectionsStorage) CreateConnection(param types.Connection) error {
}
conns := c.getConnections()
groupIndex, existsGroup := sliceutil.Find(conns, func(i int) bool {
return conns[i].GroupName == param.Group
})
if !existsGroup {
// no group matched, create new group
group := types.ConnectionGroup{
GroupName: param.Group,
Connections: []types.Connection{param},
var group *types.Connection
if len(param.Group) > 0 {
for i, conn := range conns {
if conn.Type == "group" && conn.Name == param.Group {
group = &conns[i]
break
}
}
conns = append(conns, group)
}
if group != nil {
group.Connections = append(group.Connections, types.Connection{
ConnectionConfig: param,
})
} else {
conns[groupIndex].Connections = append(conns[groupIndex].Connections, param)
if len(param.Group) > 0 {
// no group matched, create new group
conns = append(conns, types.Connection{
Type: "group",
Connections: types.Connections{
types.Connection{
ConnectionConfig: param,
},
},
})
} else {
conns = append(conns, types.Connection{
ConnectionConfig: param,
})
}
}
return c.saveConnections(conns)
}
// UpdateConnection update existing connection by name
func (c *ConnectionsStorage) UpdateConnection(name string, param types.Connection) error {
func (c *ConnectionsStorage) UpdateConnection(name string, param types.ConnectionConfig) error {
c.mutex.Lock()
defer c.mutex.Unlock()
var updated bool
conns := c.getConnections()
groupIndex := -1
connIndex := -1
// find out edit connection
for i, group := range conns {
for j, conn := range group.Connections {
// check conflict connection name
if conn.Name == name {
// different group name, should move to new group
// remove from current group first
if group.GroupName != param.Group {
conns[i].Connections = append(conns[i].Connections[:j], conns[i].Connections[j+1:]...)
// find new group index
groupIndex, _ = sliceutil.Find(conns, func(i int) bool {
return conns[i].GroupName == param.Group
})
} else {
groupIndex = i
connIndex = j
for i, conn := range conns {
if conn.Name == name {
conns[i] = types.Connection{
ConnectionConfig: param,
}
updated = true
} else if conn.Type == "group" {
for j, conn2 := range conn.Connections {
if conn2.Name == name {
conns[i].Connections[j] = types.Connection{
ConnectionConfig: param,
}
updated = true
break
}
break
}
}
if updated {
break
}
}
if groupIndex >= 0 {
// group exists
if connIndex >= 0 {
// connection exists
conns[groupIndex].Connections[connIndex] = param
} else {
// new connection
conns[groupIndex].Connections = append(conns[groupIndex].Connections, param)
}
} else {
// new group
group := types.ConnectionGroup{
GroupName: param.Group,
Connections: []types.Connection{param},
}
conns = append(conns, group)
if updated {
return c.saveConnections(conns)
}
return c.saveConnections(conns)
return errors.New("connection not found")
}
// RemoveConnection remove special connection
@ -189,55 +212,82 @@ func (c *ConnectionsStorage) RemoveConnection(name string) error {
defer c.mutex.Unlock()
conns := c.getConnections()
for i, connGroup := range conns {
for j, conn := range connGroup.Connections {
if conn.Name == name {
connList := conns[i].Connections
connList = append(connList[:j], connList[j+1:]...)
conns[i].Connections = connList
return c.saveConnections(conns)
var updated bool
for i, conn := range conns {
if conn.Type == "group" {
for j, subConn := range conn.Connections {
if subConn.Name == name {
conns[i].Connections = append(conns[i].Connections[:j], conns[i].Connections[j+1:]...)
updated = true
break
}
}
} else if conn.Name == name {
conns = append(conns[:i], conns[i+1:]...)
updated = true
break
}
if updated {
break
}
}
return errors.New("no match connection")
}
// UpsertGroup update or insert a group
// When want to create group only, set group == param.name
func (c *ConnectionsStorage) UpsertGroup(group string, param types.ConnectionGroup) error {
c.mutex.Lock()
defer c.mutex.Unlock()
conns := c.getConnections()
for i, connGroup := range conns {
if connGroup.GroupName == group {
conns[i].GroupName = param.GroupName
return c.saveConnections(conns)
}
if !updated {
return errors.New("no match connection")
}
// No match group, create one
connGroup := types.ConnectionGroup{
GroupName: param.GroupName,
Connections: []types.Connection{},
}
conns = append(conns, connGroup)
return c.saveConnections(conns)
}
// RemoveGroup remove special group, include all connections under it
func (c *ConnectionsStorage) RemoveGroup(group string) error {
// CreateGroup create new group
func (c *ConnectionsStorage) CreateGroup(name string) error {
c.mutex.Lock()
defer c.mutex.Unlock()
conns := c.getConnections()
for i, connGroup := range conns {
if connGroup.GroupName == group {
conns = append(conns[:i], conns[i+1:]...)
for _, conn := range conns {
if conn.Type == "group" && conn.Name == name {
return errors.New("duplicated group name")
}
}
conns = append(conns, types.Connection{
ConnectionConfig: types.ConnectionConfig{
Name: name,
},
Type: "group",
})
return c.saveConnections(conns)
}
// RenameGroup rename group
func (c *ConnectionsStorage) RenameGroup(name, newName string) error {
c.mutex.Lock()
defer c.mutex.Unlock()
conns := c.getConnections()
for i, conn := range conns {
if conn.Type == "group" && conn.Name == name {
conns[i].Name = newName
return c.saveConnections(conns)
}
}
return errors.New("no match group")
return errors.New("group not found")
}
// RemoveGroup remove special group, include all connections under it
func (c *ConnectionsStorage) RemoveGroup(group string, includeConnection bool) error {
c.mutex.Lock()
defer c.mutex.Unlock()
conns := c.getConnections()
for i, conn := range conns {
if conn.Type == "group" && conn.Name == group {
conns = append(conns[:i], conns[i+1:]...)
if includeConnection {
conns = append(conns, conn.Connections...)
}
return c.saveConnections(conns)
}
}
return errors.New("group not found")
}

View File

@ -2,20 +2,28 @@ package types
type ConnectionCategory int
type Connection struct {
Group string `json:"group" yaml:"-"`
type ConnectionConfig struct {
Name string `json:"name" yaml:"name"`
Addr string `json:"addr" yaml:"addr"`
Port int `json:"port" yaml:"port"`
Username string `json:"username" yaml:"username"`
Password string `json:"password" yaml:"password"`
DefaultFilter string `json:"defaultFilter" yaml:"default_filter"`
KeySeparator string `json:"keySeparator" yaml:"key_separator"`
ConnTimeout int `json:"connTimeout" yaml:"conn_timeout"`
ExecTimeout int `json:"execTimeout" yaml:"exec_timeout"`
MarkColor string `json:"markColor" yaml:"mark_color"`
Group string `json:"group" yaml:"-"`
Addr string `json:"addr,omitempty" yaml:"addr,omitempty"`
Port int `json:"port,omitempty" yaml:"port,omitempty"`
Username string `json:"username,omitempty" yaml:"username,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty"`
DefaultFilter string `json:"defaultFilter,omitempty" yaml:"default_filter,omitempty"`
KeySeparator string `json:"keySeparator,omitempty" yaml:"key_separator,omitempty"`
ConnTimeout int `json:"connTimeout,omitempty" yaml:"conn_timeout,omitempty"`
ExecTimeout int `json:"execTimeout,omitempty" yaml:"exec_timeout,omitempty"`
MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"`
}
type Connection struct {
ConnectionConfig `json:",inline" yaml:",inline"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Connections []Connection `json:"connections,omitempty" yaml:"connections,omitempty"`
}
type Connections []Connection
type ConnectionGroup struct {
GroupName string `json:"groupName" yaml:"group_name"`
Connections []Connection `json:"connections" yaml:"connections"`

View File

@ -9,6 +9,7 @@ import json from 'highlight.js/lib/languages/json'
import plaintext from 'highlight.js/lib/languages/plaintext'
import AddFieldsDialog from './components/dialogs/AddFieldsDialog.vue'
import AppContent from './AppContent.vue'
import GroupDialog from './components/dialogs/GroupDialog.vue'
hljs.registerLanguage('json', json)
hljs.registerLanguage('plaintext', plaintext)
@ -41,6 +42,7 @@ const themeOverrides = {
<!-- top modal dialogs -->
<connection-dialog />
<group-dialog />
<new-key-dialog />
<add-fields-dialog />
<rename-key-dialog />

View File

@ -11,6 +11,7 @@ import useTabStore from '../../stores/tab.js'
import { useDialog } from 'naive-ui'
import useConnectionStore from '../../stores/connections.js'
import { useI18n } from 'vue-i18n'
import { useConfirmDialog } from '../../utils/confirm_dialog.js'
const valueComponents = {
[types.STRING]: ContentValueString,
@ -58,22 +59,13 @@ const onAddTab = () => {
}
const i18n = useI18n()
const confirmDialog = useConfirmDialog()
const onCloseTab = (tabIndex) => {
dialog.warning({
title: i18n.t('close_confirm_title'),
content: i18n.t('close_confirm'),
positiveText: i18n.t('confirm'),
negativeText: i18n.t('cancel'),
closable: false,
closeOnEsc: false,
maskClosable: false,
transformOrigin: 'center',
onPositiveClick: () => {
const tab = get(tabStore.tabs, tabIndex)
if (tab != null) {
connectionStore.closeConnection(tab.name)
}
},
confirmDialog.warning(i18n.t('close_confirm'), () => {
const tab = get(tabStore.tabs, tabIndex)
if (tab != null) {
connectionStore.closeConnection(tab.name)
}
})
}
</script>

View File

@ -85,8 +85,8 @@ const onSaveConnection = async () => {
return
}
message.success(i18n.t('new_conn_succ'))
dialogStore.closeNewDialog()
message.success(i18n.t('handle_succ'))
onClose()
}
const resetForm = () => {
@ -132,7 +132,11 @@ const onTestConnection = async () => {
}
const onClose = () => {
dialogStore.closeNewDialog()
if (isEditMode.value) {
dialogStore.closeEditDialog()
} else {
dialogStore.closeNewDialog()
}
}
</script>
@ -162,7 +166,7 @@ const onClose = () => {
<n-form-item :label="$t('conn_name')" path="name" required>
<n-input v-model:value="generalForm.name" :placeholder="$t('conn_name_tip')" />
</n-form-item>
<n-form-item :label="$t('conn_group')" required>
<n-form-item v-if="!isEditMode" :label="$t('conn_group')" required>
<n-select v-model:value="generalForm.group" :options="groupOptions"></n-select>
</n-form-item>
<n-form-item :label="$t('conn_addr')" path="addr" required>

View File

@ -0,0 +1,97 @@
<script setup>
import { computed, reactive, ref, watch } from 'vue'
import useDialog from '../../stores/dialog'
import { useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import useConnectionStore from '../../stores/connections.js'
import { isEmpty } from 'lodash'
/**
* Dialog for create or rename group
*/
const editGroup = ref('')
const groupForm = reactive({
name: '',
})
const isRenameMode = computed(() => !isEmpty(editGroup.value))
const dialogStore = useDialog()
const connectionStore = useConnectionStore()
watch(
() => dialogStore.groupDialogVisible,
(visible) => {
if (visible) {
groupForm.name = editGroup.value = dialogStore.editGroup
}
}
)
const i18n = useI18n()
const message = useMessage()
const onConfirm = async () => {
try {
const { name } = groupForm
if (isRenameMode.value) {
const { success, msg } = await connectionStore.renameGroup(editGroup.value, name)
if (success) {
message.success(i18n.t('handle_succ'))
} else {
message.error(msg)
}
} else {
const { success, msg } = await connectionStore.createGroup(name)
if (success) {
message.success(i18n.t('handle_succ'))
} else {
message.error(msg)
}
}
} catch (e) {
message.error(e.message)
}
onClose()
}
const onClose = () => {
if (isRenameMode.value) {
dialogStore.closeNewGroupDialog()
} else {
dialogStore.closeRenameGroupDialog()
}
}
</script>
<template>
<n-modal
v-model:show="dialogStore.groupDialogVisible"
:closable="false"
:close-on-esc="false"
:mask-closable="false"
:negative-button-props="{ size: 'medium' }"
:negative-text="$t('cancel')"
:positive-button-props="{ size: 'medium' }"
:positive-text="$t('confirm')"
:show-icon="false"
:title="isRenameMode ? $t('rename_group') : $t('new_group')"
preset="dialog"
transform-origin="center"
@positive-click="onConfirm"
@negative-click="onClose"
>
<n-form
:model="groupForm"
:show-require-mark="false"
label-align="left"
label-placement="left"
label-width="auto"
>
<n-form-item :label="$t('group_name')" required>
<n-input v-model:value="groupForm.name" placeholder="" />
</n-form-item>
</n-form>
</n-modal>
</template>
<style lang="scss" scoped></style>

View File

@ -16,6 +16,10 @@ const connectionStore = useConnectionStore()
const onSort = () => {
dialogStore.openPreferencesDialog()
}
const onDisconnectAll = () => {
connectionStore.closeAllConnection()
}
</script>
<template>
@ -38,7 +42,7 @@ const onSort = () => {
size="20"
stroke-width="4"
t-tooltip="new_group"
@click="dialogStore.openNewKeyDialog('aa:bb')"
@click="dialogStore.openNewGroupDialog()"
/>
<icon-button
:disabled="!connectionStore.anyConnectionOpened"
@ -47,7 +51,7 @@ const onSort = () => {
size="20"
stroke-width="4"
t-tooltip="disconnect_all"
@click="dialogStore.openNewKeyDialog('aa:bb')"
@click="onDisconnectAll"
/>
<n-divider style="margin: 0 4px; --n-color: #aaa; width: 2px" vertical />
<icon-button :icon="Sort" color="#555" size="20" stroke-width="4" t-tooltip="sort_conn" @click="onSort" />

View File

@ -15,6 +15,7 @@ import Connect from '../icons/Connect.vue'
import { useI18n } from 'vue-i18n'
import useTabStore from '../../stores/tab.js'
import Edit from '../icons/Edit.vue'
import { useConfirmDialog } from '../../utils/confirm_dialog.js'
const i18n = useI18n()
const loadingConnection = ref(false)
@ -55,9 +56,9 @@ const renderIcon = (icon) => {
const menuOptions = {
[ConnectionType.Group]: ({ opened }) => [
{
key: 'group_reload',
label: i18n.t('edit_conn_group'),
icon: renderIcon(Config),
key: 'group_rename',
label: i18n.t('rename_conn_group'),
icon: renderIcon(Edit),
},
{
key: 'group_delete',
@ -182,22 +183,24 @@ const openConnection = async (name) => {
}
const dialog = useDialog()
const removeConnection = async (name) => {
dialog.warning({
title: i18n.t('warning'),
content: i18n.t('remove_conn_tip', { conn: name }),
closable: false,
autoFocus: false,
transformOrigin: 'center',
positiveText: i18n.t('confirm'),
negativeText: i18n.t('cancel'),
onPositiveClick: async () => {
connectionStore.removeConnection(name).then(({ success, msg }) => {
if (!success) {
message.error(msg)
}
})
},
const removeConnection = (name) => {
confirmDialog.warning(i18n.t('remove_tip', { type: i18n.t('conn_name'), name }), async () => {
connectionStore.removeConnection(name).then(({ success, msg }) => {
if (!success) {
message.error(msg)
}
})
})
}
const confirmDialog = useConfirmDialog()
const removeGroup = async (name) => {
confirmDialog.warning(i18n.t('remove_tip', { type: i18n.t('conn_group'), name }), async () => {
connectionStore.deleteGroup(name).then(({ success, msg }) => {
if (!success) {
message.error(msg)
}
})
})
}
@ -233,7 +236,7 @@ const renderContextLabel = (option) => {
const handleSelectContextMenu = (key) => {
contextMenuParam.show = false
const { name, db, key: nodeKey, redisKey } = contextMenuParam.currentNode
const { name, label, db, key: nodeKey, redisKey } = contextMenuParam.currentNode
switch (key) {
case 'server_open':
openConnection(name).then(() => {})
@ -247,6 +250,12 @@ const handleSelectContextMenu = (key) => {
case 'server_close':
connectionStore.closeConnection(name)
break
case 'group_rename':
dialogStore.openRenameGroupDialog(label)
break
case 'group_delete':
removeGroup(label)
break
}
console.warn('TODO: handle context menu:' + key)
}

View File

@ -4,7 +4,7 @@ import { ConnectionType } from '../../consts/connection_type.js'
import { NIcon, useDialog, useMessage } from 'naive-ui'
import Key from '../icons/Key.vue'
import ToggleDb from '../icons/ToggleDb.vue'
import { indexOf } from 'lodash'
import { get, indexOf } from 'lodash'
import { useI18n } from 'vue-i18n'
import Refresh from '../icons/Refresh.vue'
import CopyLink from '../icons/CopyLink.vue'
@ -15,6 +15,7 @@ import Connect from '../icons/Connect.vue'
import useDialogStore from '../../stores/dialog.js'
import { ClipboardSetText } from '../../../wailsjs/runtime/runtime.js'
import useConnectionStore from '../../stores/connections.js'
import { useConfirmDialog } from '../../utils/confirm_dialog.js'
const i18n = useI18n()
const loading = ref(false)
@ -269,6 +270,7 @@ const onLoadTree = async (node) => {
}
}
const confirmDialog = useConfirmDialog()
const handleSelectContextMenu = (key) => {
contextMenuParam.show = false
const { name, db, key: nodeKey, redisKey } = contextMenuParam.currentNode
@ -283,21 +285,12 @@ const handleSelectContextMenu = (key) => {
break
case 'key_remove':
case 'value_remove':
dialog.warning({
title: i18n.t('warning'),
content: i18n.t('delete_key_tip', { key: redisKey }),
closable: false,
autoFocus: false,
transformOrigin: 'center',
positiveText: i18n.t('confirm'),
negativeText: i18n.t('cancel'),
onPositiveClick: () => {
connectionStore.removeKey(name, db, redisKey).then((success) => {
if (success) {
message.success(i18n.t('delete_key_succ', { key: redisKey }))
}
})
},
confirmDialog.warning(i18n.t('delete_key_tip', { key: redisKey }), () => {
connectionStore.removeKey(name, db, redisKey).then((success) => {
if (success) {
message.success(i18n.t('delete_key_succ', { key: redisKey }))
}
})
})
break
case 'key_copy':

View File

@ -7,13 +7,13 @@
"save": "Save",
"new_conn": "Add New Connection",
"new_group": "Add New Group",
"rename_group": "Rename Group",
"disconnect_all": "Disconnect all connections",
"sort_conn": "Resort Connections",
"reload_key": "Reload Current Key",
"close_confirm_title": "Confirm Close",
"close_confirm": "Confirm close this tab and connection",
"opening_connection": "Opening Connection...",
"remove_conn_tip": "Connection \"{conn}\" will be deleted",
"remove_tip": "{type} \"{name}\" will be deleted",
"ttl": "TTL",
"forever": "Forever",
"rename_key": "Rename Key",
@ -38,6 +38,7 @@
"remove_conn": "Delete Connection",
"edit_conn": "Edit Connection Config",
"edit_conn_group": "Edit Connection Group",
"rename_conn_group": "Rename Connection Group",
"remove_conn_group": "Delete Connection Group",
"no_group": "No Group",
"copy_path": "Copy Path",
@ -50,6 +51,7 @@
"advanced": "Advanced",
"editor": "Editor",
"conn_group": "Group",
"group_name": "Group Name",
"conn_name": "Name",
"conn_addr": "Address",
"conn_usr": "Username",
@ -68,7 +70,6 @@
"conn_advn_conn_timeout": "Connection Timeout",
"conn_advn_exec_timeout": "Execution Timeout",
"conn_advn_mark_color": "Mark Color",
"new_conn_succ": "Create new connection success!",
"second": "Second(s)",
"new_key_name": "New Key Name",
"new_key": "Add New Key",

View File

@ -7,13 +7,13 @@
"save": "保存",
"new_conn": "添加新连接",
"new_group": "添加新分组",
"rename_group": "重命名分组",
"disconnect_all": "断开所有连接",
"sort_conn": "调整连接顺序",
"reload_key": "重新载入此键内容",
"close_confirm_title": "关闭确认",
"close_confirm": "是否关闭当前连接",
"opening_connection": "正在打开连接...",
"remove_conn_tip": "连接 \"{conn}\" 将会被删除",
"remove_tip": "{type} \"{name}\" 将会被删除",
"ttl": "TTL",
"forever": "永久",
"rename_key": "重命名键",
@ -40,6 +40,7 @@
"remove_conn": "删除连接",
"edit_conn": "编辑连接配置",
"edit_conn_group": "编辑连接分组",
"rename_conn_group": "重命名连接分组",
"remove_conn_group": "删除连接分组",
"no_group": "无分组",
"copy_path": "复制路径",
@ -51,7 +52,8 @@
"general": "常规配置",
"advanced": "高级配置",
"editor": "编辑器",
"conn_group": "所属分组",
"conn_group": "分组",
"group_name": "分组名",
"conn_name": "连接名",
"conn_addr": "连接地址",
"conn_usr": "用户名",
@ -70,7 +72,6 @@
"conn_advn_conn_timeout": "连接超时",
"conn_advn_exec_timeout": "执行超时",
"conn_advn_mark_color": "标记颜色",
"new_conn_succ": "新建连接成功",
"second": "秒",
"new_key_name": "新键名",
"new_key": "添加新键",

View File

@ -5,13 +5,16 @@ import {
AddListItem,
AddZSetValue,
CloseConnection,
CreateGroup,
GetConnection,
GetKeyValue,
ListConnection,
OpenConnection,
OpenDatabase,
RemoveConnection,
RemoveGroup,
RemoveKey,
RenameGroup,
RenameKey,
SaveConnection,
SetHashValue,
@ -76,29 +79,23 @@ const useConnectionStore = defineStore('connections', {
const conns = []
const groups = []
const { data = [{ groupName: '', connections: [] }] } = await ListConnection()
for (let i = 0; i < data.length; i++) {
const group = data[i]
// Top level group
if (isEmpty(group.groupName)) {
const len = size(group.connections)
for (let j = 0; j < len; j++) {
const item = group.connections[j]
conns.push({
key: item.name,
label: item.name,
name: item.name,
type: ConnectionType.Server,
// isLeaf: false,
})
}
for (const conn of data) {
if (conn.type !== 'group') {
// top level
conns.push({
key: conn.name,
label: conn.name,
name: conn.name,
type: ConnectionType.Server,
// isLeaf: false,
})
} else {
groups.push(group.groupName)
// Custom group
// custom group
groups.push(conn.name)
const subConns = get(conn, 'connections', [])
const children = []
const len = size(group.connections)
for (let j = 0; j < len; j++) {
const item = group.connections[j]
const value = group.groupName + '/' + item.name
for (const item of subConns) {
const value = conn.name + '/' + item.name
children.push({
key: value,
label: item.name,
@ -108,8 +105,8 @@ const useConnectionStore = defineStore('connections', {
})
}
conns.push({
key: group.groupName,
label: group.groupName,
key: conn.name,
label: conn.name,
type: ConnectionType.Group,
children,
})
@ -127,7 +124,6 @@ const useConnectionStore = defineStore('connections', {
async getConnectionProfile(name) {
try {
const { data, success } = await GetConnection(name)
console.log(JSON.stringify(data))
if (success) {
return data
}
@ -263,6 +259,20 @@ const useConnectionStore = defineStore('connections', {
return true
},
/**
* Close all connection
* @returns {Promise<void>}
*/
async closeAllConnection() {
for (const name in this.databases) {
await CloseConnection(name)
}
this.databases = {}
const tabStore = useTabStore()
tabStore.removeAllTab()
},
/**
* Remove connection
* @param name
@ -279,6 +289,53 @@ const useConnectionStore = defineStore('connections', {
return { success: true }
},
/**
* Create connection group
* @param name
* @returns {Promise<{success: boolean, [msg]: string}>}
*/
async createGroup(name) {
const { success, msg } = await CreateGroup(name)
if (!success) {
return { success: false, msg }
}
await this.initConnections(true)
return { success: true }
},
/**
* Rename connection group
* @param name
* @param newName
* @returns {Promise<{success: boolean, [msg]: string}>}
*/
async renameGroup(name, newName) {
if (name === newName) {
return { success: true }
}
const { success, msg } = await RenameGroup(name, newName)
if (!success) {
return { success: false, msg }
}
await this.initConnections(true)
return { success: true }
},
/**
* Remove group by name
* @param {string} name
* @param {boolean} [includeConn]
* @returns {Promise<{success: boolean, [msg]: string}>}
*/
async deleteGroup(name, includeConn) {
const { success, msg } = await RemoveGroup(name, includeConn === true)
if (!success) {
return { success: false, msg }
}
await this.initConnections(true)
return { success: true }
},
/**
* Open database and load all keys
* @param connName

View File

@ -6,6 +6,9 @@ const useDialogStore = defineStore('dialog', {
connDialogVisible: false,
connParam: null,
groupDialogVisible: false,
editGroup: '',
/**
* @property {string} prefix
* @property {string} server
@ -58,6 +61,21 @@ const useDialogStore = defineStore('dialog', {
this.connDialogVisible = false
},
openNewGroupDialog() {
this.groupDialogVisible = true
},
closeNewGroupDialog() {
this.groupDialogVisible = false
},
openRenameGroupDialog(name) {
this.editGroup = name
this.groupDialogVisible = true
},
closeRenameGroupDialog() {
this.groupDialogVisible = false
},
/**
*
* @param {string} server

View File

@ -199,7 +199,7 @@ const useTabStore = defineStore('tab', {
},
removeAllTab() {
this.tabList = []
this.newBlankTab()
this._setActivatedIndex(-1, false)
},
},
})

View File

@ -0,0 +1,28 @@
import { createDiscreteApi } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { onMounted } from 'vue'
const { dialog } = createDiscreteApi(['dialog'])
export function useConfirmDialog() {
let i18n
onMounted(() => {
i18n = useI18n()
})
return {
warning: (content, onConfirm) => {
dialog.warning({
title: i18n.t('warning'),
content: content,
closable: false,
autoFocus: false,
transformOrigin: 'center',
positiveText: i18n.t('confirm'),
negativeText: i18n.t('cancel'),
onPositiveClick: () => {
onConfirm && onConfirm()
},
})
},
}
}