diff --git a/backend/services/browser_service.go b/backend/services/browser_service.go index a1e6b78..42141cd 100644 --- a/backend/services/browser_service.go +++ b/backend/services/browser_service.go @@ -490,10 +490,12 @@ func (b *browserService) LoadAllKeys(connName string, db int, match, keyType str return } b.setClientCursor(connName, db, 0) + maxKeys := b.loadDBSize(ctx, client) resp.Success = true resp.Data = map[string]any{ - "keys": keys, + "keys": keys, + "maxKeys": maxKeys, } return } diff --git a/frontend/src/components/content_value/ContentValueWrapper.vue b/frontend/src/components/content_value/ContentValueWrapper.vue index fdf8d05..689b40d 100644 --- a/frontend/src/components/content_value/ContentValueWrapper.vue +++ b/frontend/src/components/content_value/ContentValueWrapper.vue @@ -142,8 +142,8 @@ watch( { const { match: pattern, type } = browserStore.getKeyFilter(props.server, db) dialogStore.openKeyFilterDialog(props.server, db, pattern, type) break - // case 'key_reload': - // browserStore.loadKeys(props.server, db, redisKey) - // break + case 'key_reload': + if (node != null && !!!node.loading) { + node.loading = true + browserStore.reloadLayer(props.server, db, redisKey).finally(() => { + delete node.loading + }) + } + break case 'value_reload': browserStore.reloadKey({ server: props.server, @@ -519,7 +524,7 @@ const renderIconMenu = (items) => { ) } -const getDatabaseMenu = (opened, loading, end) => { +const calcDBMenu = (opened, loading, end) => { const btns = [] if (opened) { btns.push( @@ -581,14 +586,16 @@ const getDatabaseMenu = (opened, loading, end) => { return btns } -const getLayerMenu = () => { +const calcLayerMenu = (loading, end) => { return [ - // disable reload by layer, due to conflict with partial loading keys - // h(IconButton, { - // tTooltip: 'interface.reload', - // icon: Refresh, - // onClick: () => handleSelectContextMenu('key_reload'), - // }), + // reload layer enable only full loaded + h(IconButton, { + tTooltip: end === true ? 'interface.reload' : 'interface.reload', + icon: Refresh, + loading: loading === true, + disabled: end !== true, + onClick: () => handleSelectContextMenu('key_reload'), + }), h(IconButton, { tTooltip: 'interface.new_key', icon: Add, @@ -602,7 +609,7 @@ const getLayerMenu = () => { ] } -const getValueMenu = () => { +const calcValueMenu = () => { return [ h(IconButton, { tTooltip: 'interface.remove_key', @@ -617,11 +624,12 @@ const renderSuffix = ({ option }) => { if ((option.type === ConnectionType.RedisDB && option.opened) || includes(selectedKeys.value, option.key)) { switch (option.type) { case ConnectionType.RedisDB: - return renderIconMenu(getDatabaseMenu(option.opened, option.loading, option.fullLoaded)) + return renderIconMenu(calcDBMenu(option.opened, option.loading, option.fullLoaded)) case ConnectionType.RedisKey: - return renderIconMenu(getLayerMenu()) + const fullLoaded = browserStore.isFullLoaded(props.server, option.db) + return renderIconMenu(calcLayerMenu(option.loading, fullLoaded)) case ConnectionType.RedisValue: - return renderIconMenu(getValueMenu()) + return renderIconMenu(calcValueMenu()) } } return null diff --git a/frontend/src/stores/browser.js b/frontend/src/stores/browser.js index 0dbd685..ee5c553 100644 --- a/frontend/src/stores/browser.js +++ b/frontend/src/stores/browser.js @@ -139,7 +139,7 @@ const useBrowserStore = defineStore('browser', { * get database by server name and index * @param {string} connName * @param {number} db - * @return {{}|null} + * @return {DatabaseItem|null} */ getDatabase(connName, db) { const dbs = this.databases[connName] @@ -152,6 +152,20 @@ const useBrowserStore = defineStore('browser', { return null }, + /** + * get full loaded status of database + * @param connName + * @param db + * @return {boolean} + */ + isFullLoaded(connName, db) { + const selDB = this.getDatabase(connName, db) + if (selDB != null) { + return selDB.fullLoaded === true + } + return false + }, + /** * switch key view * @param {string} connName @@ -473,9 +487,9 @@ const useBrowserStore = defineStore('browser', { * @param {string} connName * @param {number} db * @param {string} match - * @param {string} matchType + * @param {string} [matchType] * @param {boolean} [full] - * @returns {Promise<{keys: string[], end: boolean}>} + * @returns {Promise<{keys: string[], maxKeys: number, end: boolean}>} */ async scanKeys(connName, db, match, matchType, full) { let resp @@ -488,8 +502,8 @@ const useBrowserStore = defineStore('browser', { if (!success) { throw new Error(msg) } - const { keys = [], end } = data - return { keys, end, success } + const { keys = [], maxKeys, end } = data + return { keys, end, maxKeys, success } }, /** @@ -499,7 +513,7 @@ const useBrowserStore = defineStore('browser', { * @param {string|null} prefix * @param {string|null} matchType * @param {boolean} [all] - * @return {Promise<{keys: Array, end: boolean}>} + * @return {Promise<{keys: Array, maxKeys: number, end: boolean}>} * @private */ async _loadKeys(connName, db, prefix, matchType, all) { @@ -523,7 +537,8 @@ const useBrowserStore = defineStore('browser', { */ async loadMoreKeys(connName, db) { const { match, type: keyType } = this.getKeyFilter(connName, db) - const { keys, end } = await this._loadKeys(connName, db, match, keyType, false) + const { keys, maxKeys, end } = await this._loadKeys(connName, db, match, keyType, false) + this._setDBMaxKeys(connName, db, maxKeys) // remove current keys below prefix this._addKeyNodes(connName, db, keys) this._tidyNode(connName, db, '') @@ -538,11 +553,44 @@ const useBrowserStore = defineStore('browser', { */ async loadAllKeys(connName, db) { const { match, type: keyType } = this.getKeyFilter(connName, db) - const { keys } = await this._loadKeys(connName, db, match, keyType, true) + const { keys, maxKeys } = await this._loadKeys(connName, db, match, keyType, true) + this._setDBMaxKeys(connName, db, maxKeys) this._addKeyNodes(connName, db, keys) this._tidyNode(connName, db, '') }, + /** + * reload keys under layer + * @param {string} connName + * @param {number} db + * @param {string} prefix + * @return {Promise} + */ + async reloadLayer(connName, db, prefix) { + if (isEmpty(prefix)) { + return + } + let match = prefix + const separator = this._getSeparator(connName) + if (!endsWith(match, separator)) { + match += separator + '*' + } else { + match += '*' + } + // FIXME: ignore original match pattern due to redis not support combination matching + const { match: originMatch, type: keyType } = this.getKeyFilter(connName, db) + const { keys, maxKeys, success } = await this._loadKeys(connName, db, match, keyType, true) + if (!success) { + return + } + + this._setDBMaxKeys(connName, db, maxKeys) + // remove current keys below prefix + this._deleteKeyNode(connName, db, prefix, true) + this._addKeyNodes(connName, db, keys) + this._tidyNode(connName, db, prefix) + }, + /** * get custom separator of connection * @param server @@ -816,13 +864,16 @@ const useBrowserStore = defineStore('browser', { }, /** - * update max key by value + * update max key by increase/decrease value * @param {string} connName * @param {number} db - * @param {number} updateValue + * @param {number} [updateValue] * @private */ _updateDBMaxKeys(connName, db, updateValue) { + if (updateValue === undefined) { + return + } const database = this.getDatabase(connName, db) if (database != null) { const maxKeys = get(database, 'maxKeys', 0) @@ -831,15 +882,16 @@ const useBrowserStore = defineStore('browser', { }, /** - * set db max keys to 0 - * @param connName - * @param db + * set db max keys value + * @param {string} connName + * @param {number} db + * @param {number} maxKeys * @private */ - _emptyDBMaxKeys(connName, db) { + _setDBMaxKeys(connName, db, maxKeys) { const database = this.getDatabase(connName, db) if (database != null) { - set(database, 'maxKeys', 0) + set(database, 'maxKeys', maxKeys) } }, @@ -1588,7 +1640,7 @@ const useBrowserStore = defineStore('browser', { if (success === true) { // update tree view data this._deleteKeyNode(connName, db) - this._emptyDBMaxKeys(connName, db) + this._setDBMaxKeys(connName, db, 0) // set tab content empty const tab = useTabStore() tab.emptyTab(connName)