refactor: browser tree node update logic(new/delete/refresh)
This commit is contained in:
parent
00e29ef611
commit
2b3acc0e2b
|
@ -130,7 +130,7 @@ const dragging = computed(() => {
|
||||||
|
|
||||||
.resize-divider {
|
.resize-divider {
|
||||||
//height: 100%;
|
//height: 100%;
|
||||||
width: 2px;
|
width: 5px;
|
||||||
border-left-width: 5px;
|
border-left-width: 5px;
|
||||||
background-color: v-bind('themeVars.tabColor');
|
background-color: v-bind('themeVars.tabColor');
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ watch(
|
||||||
const { prefix, server, db } = dialogStore.newKeyParam
|
const { prefix, server, db } = dialogStore.newKeyParam
|
||||||
newForm.server = server
|
newForm.server = server
|
||||||
newForm.key = isEmpty(prefix) ? '' : prefix
|
newForm.key = isEmpty(prefix) ? '' : prefix
|
||||||
|
newForm.db = db
|
||||||
newForm.type = options.value[0].value
|
newForm.type = options.value[0].value
|
||||||
newForm.ttl = -1
|
newForm.ttl = -1
|
||||||
newForm.value = null
|
newForm.value = null
|
||||||
|
|
|
@ -384,10 +384,13 @@ const onLoadTree = async (node) => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case ConnectionType.RedisKey:
|
// case ConnectionType.RedisKey:
|
||||||
// load all children
|
// console.warn('load redis key', node.redisKey)
|
||||||
// node.children = []
|
// node.keys = sumBy(node.children, 'keys')
|
||||||
break
|
// break
|
||||||
|
// case ConnectionType.RedisValue:
|
||||||
|
// node.keys = 1
|
||||||
|
// break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { endsWith, findIndex, get, isEmpty, size, split, toUpper, uniq } from 'lodash'
|
import { endsWith, get, isEmpty, join, remove, size, slice, split, sumBy, toUpper, uniq } from 'lodash'
|
||||||
import {
|
import {
|
||||||
AddHashField,
|
AddHashField,
|
||||||
AddListItem,
|
AddListItem,
|
||||||
|
@ -45,15 +45,17 @@ const useConnectionStore = defineStore('connections', {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} DatabaseItem
|
* @typedef {Object} DatabaseItem
|
||||||
* @property {string} key
|
* @property {string} key - tree node unique key
|
||||||
* @property {string} label
|
* @property {string} label
|
||||||
* @property {string} name - server name, type != ConnectionType.Group only
|
* @property {string} [name] - server name, type != ConnectionType.Group only
|
||||||
* @property {number} type
|
* @property {number} type
|
||||||
* @property {number} [db] - database index, type == ConnectionType.RedisDB only
|
* @property {number} [db] - database index, type == ConnectionType.RedisDB only
|
||||||
* @property {number} keys
|
* @property {string} [redisKey] - redis key, type == ConnectionType.RedisKey || type == ConnectionType.RedisValue only
|
||||||
|
* @property {number} [keys] - children key count
|
||||||
* @property {boolean} [isLeaf]
|
* @property {boolean} [isLeaf]
|
||||||
* @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only
|
* @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only
|
||||||
* @property {boolean} [expanded] - current node is expanded
|
* @property {boolean} [expanded] - current node is expanded
|
||||||
|
* @property {DatabaseItem[]} [children]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -453,7 +455,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
|
|
||||||
// append db node to current connection's children
|
// append db node to current connection's children
|
||||||
this._addKeyNodes(connName, db, keys)
|
this._addKeyNodes(connName, db, keys)
|
||||||
this._tidyNodeChildren(dbs[db])
|
this._tidyNode(connName, db)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -577,40 +579,9 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove current keys below prefix
|
// remove current keys below prefix
|
||||||
this._deleteKeyNodes(connName, db, prefix)
|
this._deleteKeyNode(connName, db, prefix, true)
|
||||||
this._addKeyNodes(connName, db, keys)
|
this._addKeyNodes(connName, db, keys)
|
||||||
this._tidyNodeChildren(this.databases[connName][db])
|
this._tidyNode(connName, db, prefix)
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* remove key with prefix
|
|
||||||
* @param {string} connName
|
|
||||||
* @param {number} db
|
|
||||||
* @param {string} prefix
|
|
||||||
* @returns {boolean}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_deleteKeyNodes(connName, db, prefix) {
|
|
||||||
const separator = this._getSeparator(connName)
|
|
||||||
const dbs = this.databases[connName]
|
|
||||||
let node = dbs[db]
|
|
||||||
const prefixPart = split(prefix, separator)
|
|
||||||
const partLen = size(prefixPart)
|
|
||||||
for (let i = 0; i < partLen; i++) {
|
|
||||||
let idx = findIndex(node.children, { label: prefixPart[i] })
|
|
||||||
if (idx === -1) {
|
|
||||||
node = null
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (i === partLen - 1) {
|
|
||||||
// remove last part from parent
|
|
||||||
node.children.splice(idx, 1)
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
node = node.children[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -627,6 +598,21 @@ const useConnectionStore = defineStore('connections', {
|
||||||
return keySeparator
|
return keySeparator
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get node map
|
||||||
|
* @param connName
|
||||||
|
* @param db
|
||||||
|
* @returns {Map<string, DatabaseItem>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getNodeMap(connName, db) {
|
||||||
|
if (this.nodeMap[`${connName}#${db}`] == null) {
|
||||||
|
this.nodeMap[`${connName}#${db}`] = new Map()
|
||||||
|
}
|
||||||
|
// construct a tree node list, the format of item key likes 'server/db#type/key'
|
||||||
|
return this.nodeMap[`${connName}#${db}`]
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove keys in db
|
* remove keys in db
|
||||||
* @param {string} connName
|
* @param {string} connName
|
||||||
|
@ -635,34 +621,32 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_addKeyNodes(connName, db, keys) {
|
_addKeyNodes(connName, db, keys) {
|
||||||
|
if (isEmpty(keys)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const separator = this._getSeparator(connName)
|
const separator = this._getSeparator(connName)
|
||||||
const dbs = this.databases[connName]
|
const dbs = this.databases[connName]
|
||||||
if (dbs[db].children == null) {
|
if (dbs[db].children == null) {
|
||||||
dbs[db].children = []
|
dbs[db].children = []
|
||||||
}
|
}
|
||||||
if (this.nodeMap[`${connName}#${db}`] == null) {
|
const nodeMap = this._getNodeMap(connName, db)
|
||||||
this.nodeMap[`${connName}#${db}`] = new Map()
|
|
||||||
}
|
|
||||||
// construct a tree node list, the format of item key likes 'server/db#type/key'
|
|
||||||
const nodeMap = this.nodeMap[`${connName}#${db}`]
|
|
||||||
const rootChildren = dbs[db].children
|
const rootChildren = dbs[db].children
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const keyPart = split(key, separator)
|
const keyParts = split(key, separator)
|
||||||
// const prefixLen = size(keyPart) - 1
|
const len = size(keyParts)
|
||||||
const len = size(keyPart)
|
|
||||||
const lastIdx = len - 1
|
const lastIdx = len - 1
|
||||||
let handlePath = ''
|
let handlePath = ''
|
||||||
let children = rootChildren
|
let children = rootChildren
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
handlePath += keyPart[i]
|
handlePath += keyParts[i]
|
||||||
if (i !== lastIdx) {
|
if (i !== lastIdx) {
|
||||||
// layer
|
// layer
|
||||||
const nodeKey = `#${ConnectionType.RedisKey}/${handlePath}`
|
const nodeKey = `${ConnectionType.RedisKey}/${handlePath}`
|
||||||
let selectedNode = nodeMap.get(nodeKey)
|
let selectedNode = nodeMap.get(nodeKey)
|
||||||
if (selectedNode == null) {
|
if (selectedNode == null) {
|
||||||
selectedNode = {
|
selectedNode = {
|
||||||
key: `${connName}/db${db}${nodeKey}`,
|
key: `${connName}/db${db}#${nodeKey}`,
|
||||||
label: keyPart[i],
|
label: keyParts[i],
|
||||||
db,
|
db,
|
||||||
keys: 0,
|
keys: 0,
|
||||||
redisKey: handlePath,
|
redisKey: handlePath,
|
||||||
|
@ -677,11 +661,11 @@ const useConnectionStore = defineStore('connections', {
|
||||||
handlePath += separator
|
handlePath += separator
|
||||||
} else {
|
} else {
|
||||||
// key
|
// key
|
||||||
const nodeKey = `#${ConnectionType.RedisValue}/${handlePath}`
|
const nodeKey = `${ConnectionType.RedisValue}/${handlePath}`
|
||||||
const replaceKey = nodeMap.has(nodeKey)
|
const replaceKey = nodeMap.has(nodeKey)
|
||||||
const selectedNode = {
|
const selectedNode = {
|
||||||
key: `${connName}/db${db}${nodeKey}`,
|
key: `${connName}/db${db}#${nodeKey}`,
|
||||||
label: keyPart[i],
|
label: keyParts[i],
|
||||||
db,
|
db,
|
||||||
keys: 0,
|
keys: 0,
|
||||||
redisKey: handlePath,
|
redisKey: handlePath,
|
||||||
|
@ -712,23 +696,78 @@ const useConnectionStore = defineStore('connections', {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sort all node item's children and calculate keys count
|
* tidy node by key
|
||||||
* @param node
|
* @param {string} connName
|
||||||
|
* @param {number} db
|
||||||
|
* @param {string} [key]
|
||||||
|
* @param {boolean} [skipResort]
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_tidyNodeChildren(node) {
|
_tidyNode(connName, db, key, skipResort) {
|
||||||
|
const nodeMap = this._getNodeMap(connName, db)
|
||||||
|
const separator = this._getSeparator(connName)
|
||||||
|
const keyParts = split(key, separator)
|
||||||
|
const totalParts = size(keyParts)
|
||||||
|
const parentKey = slice(keyParts, 0, totalParts - 1)
|
||||||
|
const dbNode = get(this.databases, [connName, db], {})
|
||||||
|
const isDBRoot = isEmpty(parentKey)
|
||||||
|
let node
|
||||||
|
if (isDBRoot) {
|
||||||
|
// use db root node
|
||||||
|
node = dbNode
|
||||||
|
} else {
|
||||||
|
node = nodeMap.get(`${ConnectionType.RedisKey}/${join(parentKey, separator)}`)
|
||||||
|
}
|
||||||
|
if (node == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const keyCountUpdated = this._tidyNodeChildren(node, skipResort)
|
||||||
|
|
||||||
|
if (keyCountUpdated) {
|
||||||
|
// update key count of parent and above
|
||||||
|
if (!isDBRoot) {
|
||||||
|
let i = totalParts - 1
|
||||||
|
for (; i > 0; i--) {
|
||||||
|
const parentKey = join(slice(keyParts, 0, i), separator)
|
||||||
|
const parentNode = nodeMap.get(`${ConnectionType.RedisKey}/${parentKey}`)
|
||||||
|
if (parentNode == null) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parentNode.keys = sumBy(parentNode.children, 'keys')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update key count of db
|
||||||
|
dbNode.keys = sumBy(dbNode.children, 'keys')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sort all node item's children and calculate keys count
|
||||||
|
* @param node
|
||||||
|
* @param {boolean} skipSort skip sorting children
|
||||||
|
* @returns {boolean} return whether key count changed
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_tidyNodeChildren(node, skipSort) {
|
||||||
let count = 0
|
let count = 0
|
||||||
if (!isEmpty(node.children)) {
|
if (!isEmpty(node.children)) {
|
||||||
|
if (skipSort !== true) {
|
||||||
this._sortNodes(node.children)
|
this._sortNodes(node.children)
|
||||||
|
}
|
||||||
|
|
||||||
for (const elem of node.children) {
|
for (const elem of node.children) {
|
||||||
this._tidyNodeChildren(elem)
|
this._tidyNodeChildren(elem, skipSort)
|
||||||
count += elem.keys
|
count += elem.keys
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
|
if (node.keys !== count) {
|
||||||
node.keys = count
|
node.keys = count
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -743,13 +782,11 @@ const useConnectionStore = defineStore('connections', {
|
||||||
*/
|
*/
|
||||||
async setKey(connName, db, key, keyType, value, ttl) {
|
async setKey(connName, db, key, keyType, value, ttl) {
|
||||||
try {
|
try {
|
||||||
console.log(connName, db, key, keyType, value, ttl)
|
|
||||||
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl)
|
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl)
|
||||||
if (success) {
|
if (success) {
|
||||||
// update tree view data
|
// update tree view data
|
||||||
// this._addKey(connName, db, key)
|
|
||||||
this._addKeyNodes(connName, db, [key])
|
this._addKeyNodes(connName, db, [key])
|
||||||
this._tidyNodeChildren(this.databases[connName][db])
|
this._tidyNode(connName, db, key)
|
||||||
return { success }
|
return { success }
|
||||||
} else {
|
} else {
|
||||||
return { success, msg }
|
return { success, msg }
|
||||||
|
@ -1092,61 +1129,86 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @param {string} connName
|
* @param {string} connName
|
||||||
* @param {number} db
|
* @param {number} db
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
|
* @param {boolean} [isLayer]
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_deleteKeyNode(connName, db, key) {
|
_deleteKeyNode(connName, db, key, isLayer) {
|
||||||
const dbs = this.databases[connName]
|
const dbRoot = get(this.databases, [connName, db], {})
|
||||||
const dbDetail = get(dbs, db, {})
|
|
||||||
const separator = this._getSeparator(connName)
|
const separator = this._getSeparator(connName)
|
||||||
|
|
||||||
if (dbDetail == null) {
|
if (dbRoot == null) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeMap = this.nodeMap[`${connName}#${db}`]
|
const nodeMap = this._getNodeMap(connName, db)
|
||||||
if (nodeMap == null) {
|
const keyParts = split(key, separator)
|
||||||
return
|
const totalParts = size(keyParts)
|
||||||
|
if (isLayer === true) {
|
||||||
|
this._deleteChildrenKeyNodes(nodeMap, key)
|
||||||
}
|
}
|
||||||
const idx = key.lastIndexOf(separator)
|
// remove from parent in tree node
|
||||||
let parentNode = null
|
const parentKey = slice(keyParts, 0, totalParts - 1)
|
||||||
let parentKey = ''
|
let parentNode
|
||||||
if (idx === -1) {
|
if (isEmpty(parentKey)) {
|
||||||
// root
|
parentNode = dbRoot
|
||||||
parentNode = dbDetail
|
|
||||||
} else {
|
} else {
|
||||||
parentKey = key.substring(0, idx)
|
parentNode = nodeMap.get(`${ConnectionType.RedisKey}/${join(parentKey, separator)}`)
|
||||||
parentNode = nodeMap.get(`#${ConnectionType.RedisKey}/${parentKey}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentNode == null || parentNode.children == null) {
|
// not found parent node
|
||||||
return
|
if (parentNode == null) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
remove(parentNode.children, {
|
||||||
|
type: isLayer ? ConnectionType.RedisKey : ConnectionType.RedisValue,
|
||||||
|
redisKey: key,
|
||||||
|
})
|
||||||
|
|
||||||
// remove children
|
// check and remove empty layer node
|
||||||
const delIdx = findIndex(parentNode.children, { redisKey: key })
|
let i = totalParts - 1
|
||||||
if (delIdx !== -1) {
|
for (; i >= 0; i--) {
|
||||||
const childKeys = parentNode.children[delIdx].keys || 1
|
const anceKey = join(slice(keyParts, 0, i), separator)
|
||||||
parentNode.children.splice(delIdx, 1)
|
if (i > 0) {
|
||||||
parentNode.keys = Math.max(parentNode.keys - childKeys, 0)
|
const anceNode = nodeMap.get(`${ConnectionType.RedisKey}/${anceKey}`)
|
||||||
}
|
if (isEmpty(anceNode.children)) {
|
||||||
|
nodeMap.delete(`${ConnectionType.RedisKey}/${anceKey}`)
|
||||||
// also remove parent node if no more children
|
|
||||||
while (isEmpty(parentNode.children)) {
|
|
||||||
const idx = parentKey.lastIndexOf(separator)
|
|
||||||
if (idx !== -1) {
|
|
||||||
parentKey = parentKey.substring(0, idx)
|
|
||||||
parentNode = nodeMap.get(`#${ConnectionType.RedisKey}/${parentKey}`)
|
|
||||||
if (parentNode != null) {
|
|
||||||
parentNode.keys = (parentNode.keys || 1) - 1
|
|
||||||
parentNode.children = []
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// reach root, remove from db
|
// remove last empty layer node from parent
|
||||||
const delIdx = findIndex(dbDetail.children, { redisKey: parentKey })
|
if (i !== totalParts - 1) {
|
||||||
dbDetail.keys = (dbDetail.keys || 1) - 1
|
const redisKey = slice(keyParts, 0, i + 1)
|
||||||
dbDetail.children.splice(delIdx, 1)
|
remove(anceNode.children, { type: ConnectionType.RedisKey, redisKey })
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// last one, remove from db node
|
||||||
|
remove(dbRoot.children, { type: ConnectionType.RedisKey, redisKey: keyParts[0] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete node and all it's children from nodeMap
|
||||||
|
* @param nodeMap
|
||||||
|
* @param key
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_deleteChildrenKeyNodes(nodeMap, key) {
|
||||||
|
const mapKey = `${ConnectionType.RedisKey}/${key}`
|
||||||
|
const node = nodeMap.get(mapKey)
|
||||||
|
for (const child of node.children || []) {
|
||||||
|
if (child.type === ConnectionType.RedisValue) {
|
||||||
|
if (!nodeMap.delete(`${ConnectionType.RedisValue}/${child.redisKey}`)) {
|
||||||
|
console.warn('delete:', `${ConnectionType.RedisValue}/${child.redisKey}`)
|
||||||
|
}
|
||||||
|
} else if (child.type === ConnectionType.RedisKey) {
|
||||||
|
this._deleteChildrenKeyNodes(nodeMap, child.redisKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nodeMap.delete(mapKey)) {
|
||||||
|
console.warn('delete map key', mapKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1163,6 +1225,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
if (success) {
|
if (success) {
|
||||||
// update tree view data
|
// update tree view data
|
||||||
this._deleteKeyNode(connName, db, key)
|
this._deleteKeyNode(connName, db, key)
|
||||||
|
this._tidyNode(connName, db, key, true)
|
||||||
|
|
||||||
// set tab content empty
|
// set tab content empty
|
||||||
const tab = useTabStore()
|
const tab = useTabStore()
|
||||||
|
@ -1202,7 +1265,8 @@ const useConnectionStore = defineStore('connections', {
|
||||||
if (endsWith(prefix, separator)) {
|
if (endsWith(prefix, separator)) {
|
||||||
prefix = prefix.substring(0, prefix.length - 1)
|
prefix = prefix.substring(0, prefix.length - 1)
|
||||||
}
|
}
|
||||||
await this._deleteKeyNode(connName, db, prefix)
|
this._deleteKeyNode(connName, db, prefix, true)
|
||||||
|
this._tidyNode(connName, db, prefix, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
Loading…
Reference in New Issue