From f6a4ab8de0540102baa4828e04eb400a2ddd95f0 Mon Sep 17 00:00:00 2001
From: tiny-craft <137850705+tiny-craft@users.noreply.github.com>
Date: Sun, 2 Jul 2023 16:52:07 +0800
Subject: [PATCH] Add common confirm dialog function Add connection group
handle logic Refactor local connection profile struct
---
backend/services/connection_service.go | 48 +++-
backend/storage/connections.go | 262 +++++++++++-------
backend/types/connection.go | 30 +-
frontend/src/App.vue | 2 +
.../src/components/content/ContentPane.vue | 22 +-
.../components/dialogs/ConnectionDialog.vue | 12 +-
.../src/components/dialogs/GroupDialog.vue | 97 +++++++
.../src/components/sidebar/ConnectionPane.vue | 8 +-
.../src/components/sidebar/ConnectionTree.vue | 49 ++--
.../src/components/sidebar/DatabaseTree.vue | 25 +-
frontend/src/langs/en.json | 7 +-
frontend/src/langs/zh-cn.json | 9 +-
frontend/src/stores/connections.js | 105 +++++--
frontend/src/stores/dialog.js | 18 ++
frontend/src/stores/tab.js | 2 +-
frontend/src/utils/confirm_dialog.js | 28 ++
16 files changed, 506 insertions(+), 218 deletions(-)
create mode 100644 frontend/src/components/dialogs/GroupDialog.vue
create mode 100644 frontend/src/utils/confirm_dialog.js
diff --git a/backend/services/connection_service.go b/backend/services/connection_service.go
index fd5771c..e5d38e7 100644
--- a/backend/services/connection_service.go
+++ b/backend/services/connection_service.go
@@ -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{
diff --git a/backend/storage/connections.go b/backend/storage/connections.go
index ceda763..b67dae3 100644
--- a/backend/storage/connections.go
+++ b/backend/storage/connections.go
@@ -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")
}
diff --git a/backend/types/connection.go b/backend/types/connection.go
index 52fb703..06cdedd 100644
--- a/backend/types/connection.go
+++ b/backend/types/connection.go
@@ -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"`
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 7dd05f6..6716a6d 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -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 = {
+
diff --git a/frontend/src/components/content/ContentPane.vue b/frontend/src/components/content/ContentPane.vue
index 0977cef..8a3c7d3 100644
--- a/frontend/src/components/content/ContentPane.vue
+++ b/frontend/src/components/content/ContentPane.vue
@@ -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)
+ }
})
}
diff --git a/frontend/src/components/dialogs/ConnectionDialog.vue b/frontend/src/components/dialogs/ConnectionDialog.vue
index 5b3e2d2..36f7cf8 100644
--- a/frontend/src/components/dialogs/ConnectionDialog.vue
+++ b/frontend/src/components/dialogs/ConnectionDialog.vue
@@ -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()
+ }
}
@@ -162,7 +166,7 @@ const onClose = () => {
-
+
diff --git a/frontend/src/components/dialogs/GroupDialog.vue b/frontend/src/components/dialogs/GroupDialog.vue
new file mode 100644
index 0000000..82a891e
--- /dev/null
+++ b/frontend/src/components/dialogs/GroupDialog.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/sidebar/ConnectionPane.vue b/frontend/src/components/sidebar/ConnectionPane.vue
index 557366e..f5580f2 100644
--- a/frontend/src/components/sidebar/ConnectionPane.vue
+++ b/frontend/src/components/sidebar/ConnectionPane.vue
@@ -16,6 +16,10 @@ const connectionStore = useConnectionStore()
const onSort = () => {
dialogStore.openPreferencesDialog()
}
+
+const onDisconnectAll = () => {
+ connectionStore.closeAllConnection()
+}
@@ -38,7 +42,7 @@ const onSort = () => {
size="20"
stroke-width="4"
t-tooltip="new_group"
- @click="dialogStore.openNewKeyDialog('aa:bb')"
+ @click="dialogStore.openNewGroupDialog()"
/>
{
size="20"
stroke-width="4"
t-tooltip="disconnect_all"
- @click="dialogStore.openNewKeyDialog('aa:bb')"
+ @click="onDisconnectAll"
/>
diff --git a/frontend/src/components/sidebar/ConnectionTree.vue b/frontend/src/components/sidebar/ConnectionTree.vue
index 6817f8c..43d9f98 100644
--- a/frontend/src/components/sidebar/ConnectionTree.vue
+++ b/frontend/src/components/sidebar/ConnectionTree.vue
@@ -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)
}
diff --git a/frontend/src/components/sidebar/DatabaseTree.vue b/frontend/src/components/sidebar/DatabaseTree.vue
index 954c54a..45dda34 100644
--- a/frontend/src/components/sidebar/DatabaseTree.vue
+++ b/frontend/src/components/sidebar/DatabaseTree.vue
@@ -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':
diff --git a/frontend/src/langs/en.json b/frontend/src/langs/en.json
index 0107913..157ee43 100644
--- a/frontend/src/langs/en.json
+++ b/frontend/src/langs/en.json
@@ -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",
diff --git a/frontend/src/langs/zh-cn.json b/frontend/src/langs/zh-cn.json
index ea8c840..05f20ff 100644
--- a/frontend/src/langs/zh-cn.json
+++ b/frontend/src/langs/zh-cn.json
@@ -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": "添加新键",
diff --git a/frontend/src/stores/connections.js b/frontend/src/stores/connections.js
index dddaad9..21a01ab 100644
--- a/frontend/src/stores/connections.js
+++ b/frontend/src/stores/connections.js
@@ -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}
+ */
+ 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
diff --git a/frontend/src/stores/dialog.js b/frontend/src/stores/dialog.js
index 79c19d3..8322750 100644
--- a/frontend/src/stores/dialog.js
+++ b/frontend/src/stores/dialog.js
@@ -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
diff --git a/frontend/src/stores/tab.js b/frontend/src/stores/tab.js
index bf3a787..82c75df 100644
--- a/frontend/src/stores/tab.js
+++ b/frontend/src/stores/tab.js
@@ -199,7 +199,7 @@ const useTabStore = defineStore('tab', {
},
removeAllTab() {
this.tabList = []
- this.newBlankTab()
+ this._setActivatedIndex(-1, false)
},
},
})
diff --git a/frontend/src/utils/confirm_dialog.js b/frontend/src/utils/confirm_dialog.js
new file mode 100644
index 0000000..ffec1bd
--- /dev/null
+++ b/frontend/src/utils/confirm_dialog.js
@@ -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()
+ },
+ })
+ },
+ }
+}