refactor: move selectedKey to tab store
perf: add redis type color in new key type selection perf: update selection after add new key
This commit is contained in:
parent
9d897894b6
commit
7d3f5b9d9a
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
"log"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -548,61 +547,48 @@ func (c *connectionService) SetKeyValue(connName string, db int, key, keyType st
|
||||||
resp.Msg = "invalid hash value"
|
resp.Msg = "invalid hash value"
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
_, err = rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
|
||||||
if len(strs) > 1 {
|
if len(strs) > 1 {
|
||||||
|
kvs := map[string]any{}
|
||||||
for i := 0; i < len(strs); i += 2 {
|
for i := 0; i < len(strs); i += 2 {
|
||||||
pipe.HSetNX(ctx, key, strs[i].(string), strs[i+1])
|
kvs[strs[i].(string)] = strs[i+1]
|
||||||
}
|
}
|
||||||
} else {
|
err = rdb.HSet(ctx, key, kvs).Err()
|
||||||
pipe.HSet(ctx, key)
|
if err == nil && expiration > 0 {
|
||||||
|
rdb.Expire(ctx, key, expiration)
|
||||||
}
|
}
|
||||||
if expiration > 0 {
|
|
||||||
pipe.Expire(ctx, key, expiration)
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
case "set":
|
case "set":
|
||||||
if strs, ok := value.([]any); !ok || len(strs) <= 0 {
|
if strs, ok := value.([]any); !ok || len(strs) <= 0 {
|
||||||
resp.Msg = "invalid set value"
|
resp.Msg = "invalid set value"
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
_, err = rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
if len(strs) > 0 {
|
||||||
for _, str := range strs {
|
err = rdb.SAdd(ctx, key, strs...).Err()
|
||||||
pipe.SAdd(ctx, key, str.(string))
|
if err == nil && expiration > 0 {
|
||||||
|
rdb.Expire(ctx, key, expiration)
|
||||||
}
|
}
|
||||||
if expiration > 0 {
|
|
||||||
pipe.Expire(ctx, key, expiration)
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
case "zset":
|
case "zset":
|
||||||
if strs, ok := value.([]any); !ok || len(strs) <= 0 {
|
if strs, ok := value.([]any); !ok || len(strs) <= 0 {
|
||||||
resp.Msg = "invalid zset value"
|
resp.Msg = "invalid zset value"
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log.Println(strs)
|
if len(strs) > 1 {
|
||||||
_, err = rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
|
||||||
var members []redis.Z
|
var members []redis.Z
|
||||||
for i := 0; i < len(strs); i += 2 {
|
for i := 0; i < len(strs); i += 2 {
|
||||||
score, _ := strconv.ParseFloat(strs[i].(string), 64)
|
score, _ := strconv.ParseFloat(strs[i+1].(string), 64)
|
||||||
members = append(members, redis.Z{
|
members = append(members, redis.Z{
|
||||||
Score: score,
|
Score: score,
|
||||||
Member: strs[i+1],
|
Member: strs[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
err = rdb.ZAdd(ctx, key, members...).Err()
|
||||||
if len(members) > 0 {
|
if err == nil && expiration > 0 {
|
||||||
pipe.ZAdd(ctx, key, members...)
|
rdb.Expire(ctx, key, expiration)
|
||||||
} else {
|
|
||||||
pipe.ZAdd(ctx, key)
|
|
||||||
}
|
}
|
||||||
if expiration > 0 {
|
|
||||||
pipe.Expire(ctx, key, expiration)
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,17 +13,17 @@
|
||||||
"highlight.js": "^11.8.0",
|
"highlight.js": "^11.8.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"pinia": "^2.1.4",
|
"pinia": "^2.1.4",
|
||||||
"sass": "^1.63.6",
|
"sass": "^1.64.0",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^9.2.2"
|
"vue-i18n": "^9.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"naive-ui": "^2.34.4",
|
"naive-ui": "^2.34.4",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.0.0",
|
||||||
"unplugin-auto-import": "^0.16.4",
|
"unplugin-auto-import": "^0.16.6",
|
||||||
"unplugin-icons": "^0.16.3",
|
"unplugin-icons": "^0.16.5",
|
||||||
"unplugin-vue-components": "^0.25.1",
|
"unplugin-vue-components": "^0.25.1",
|
||||||
"vite": "^4.3.9"
|
"vite": "^4.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
5f51299692e0fa4f59808fc597b869bc
|
a78b05ffa1b32562b864c79d62d5fc2e
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive, ref, watch } from 'vue'
|
import { computed, h, reactive, ref, watch } from 'vue'
|
||||||
import { types } from '../../consts/support_redis_type'
|
import { types, typesColor } from '../../consts/support_redis_type'
|
||||||
import useDialog from '../../stores/dialog'
|
import useDialog from '../../stores/dialog'
|
||||||
import { isEmpty, keys, map } from 'lodash'
|
import { isEmpty, keys, map } from 'lodash'
|
||||||
import NewStringValue from '../new_value/NewStringValue.vue'
|
import NewStringValue from '../new_value/NewStringValue.vue'
|
||||||
|
@ -10,7 +10,8 @@ import NewZSetValue from '../new_value/NewZSetValue.vue'
|
||||||
import NewSetValue from '../new_value/NewSetValue.vue'
|
import NewSetValue from '../new_value/NewSetValue.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import useConnectionStore from '../../stores/connections.js'
|
import useConnectionStore from '../../stores/connections.js'
|
||||||
import { useMessage } from 'naive-ui'
|
import { NSpace, useMessage } from 'naive-ui'
|
||||||
|
import useTabStore from '../../stores/tab.js'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const newForm = reactive({
|
const newForm = reactive({
|
||||||
|
@ -33,7 +34,7 @@ const dbOptions = computed(() =>
|
||||||
map(keys(connectionStore.databases[newForm.server]), (key) => ({
|
map(keys(connectionStore.databases[newForm.server]), (key) => ({
|
||||||
label: key,
|
label: key,
|
||||||
value: parseInt(key),
|
value: parseInt(key),
|
||||||
}))
|
})),
|
||||||
)
|
)
|
||||||
const newFormRef = ref(null)
|
const newFormRef = ref(null)
|
||||||
const subFormRef = ref(null)
|
const subFormRef = ref(null)
|
||||||
|
@ -73,10 +74,32 @@ watch(
|
||||||
newForm.ttl = -1
|
newForm.ttl = -1
|
||||||
newForm.value = null
|
newForm.value = null
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const renderTypeLabel = (option) => {
|
||||||
|
return h(
|
||||||
|
NSpace,
|
||||||
|
{ align: 'center', inline: true, size: 3 },
|
||||||
|
{
|
||||||
|
default: () => [
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: typesColor[option.value],
|
||||||
|
width: '13px',
|
||||||
|
height: '13px',
|
||||||
|
border: '2px solid white',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
option.value,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
|
const tabStore = useTabStore()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const onAdd = async () => {
|
const onAdd = async () => {
|
||||||
await newFormRef.value?.validate().catch((err) => {
|
await newFormRef.value?.validate().catch((err) => {
|
||||||
|
@ -92,7 +115,14 @@ const onAdd = async () => {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = defaultValue[type]
|
value = defaultValue[type]
|
||||||
}
|
}
|
||||||
await connectionStore.setKey(server, db, key, type, value, ttl)
|
const { success, msg, nodeKey } = await connectionStore.setKey(server, db, key, type, value, ttl)
|
||||||
|
if (success) {
|
||||||
|
// select current key
|
||||||
|
tabStore.setSelectedKeys(server, nodeKey)
|
||||||
|
connectionStore.loadKeyValue(server, db, key).then(() => {})
|
||||||
|
} else if (!isEmpty(msg)) {
|
||||||
|
message.error(msg)
|
||||||
|
}
|
||||||
dialogStore.closeNewKeyDialog()
|
dialogStore.closeNewKeyDialog()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +168,7 @@ const onClose = () => {
|
||||||
<n-select v-model:value="newForm.db" :options="dbOptions" />
|
<n-select v-model:value="newForm.db" :options="dbOptions" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('type')" path="type" required>
|
<n-form-item :label="$t('type')" path="type" required>
|
||||||
<n-select v-model:value="newForm.type" :options="options" />
|
<n-select v-model:value="newForm.type" :options="options" :render-label="renderTypeLabel" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('ttl')" required>
|
<n-form-item :label="$t('ttl')" required>
|
||||||
<n-input-group>
|
<n-input-group>
|
||||||
|
|
|
@ -29,7 +29,7 @@ const onUpdate = () => {
|
||||||
const val = reject(zset.value, (v) => v == null || isEmpty(v.value))
|
const val = reject(zset.value, (v) => v == null || isEmpty(v.value))
|
||||||
emit(
|
emit(
|
||||||
'update:value',
|
'update:value',
|
||||||
flatMap(val, (item) => [item.value, item.score.toString()])
|
flatMap(val, (item) => [item.value, item.score.toString()]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ConnectionType } from '../../consts/connection_type.js'
|
||||||
import { NIcon, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
|
import { NIcon, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
|
||||||
import Key from '../icons/Key.vue'
|
import Key from '../icons/Key.vue'
|
||||||
import ToggleDb from '../icons/ToggleDb.vue'
|
import ToggleDb from '../icons/ToggleDb.vue'
|
||||||
import { get, indexOf, isEmpty, remove } from 'lodash'
|
import { find, get, indexOf, isEmpty, remove } from 'lodash'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import Refresh from '../icons/Refresh.vue'
|
import Refresh from '../icons/Refresh.vue'
|
||||||
import CopyLink from '../icons/CopyLink.vue'
|
import CopyLink from '../icons/CopyLink.vue'
|
||||||
|
@ -21,6 +21,7 @@ import Unlink from '../icons/Unlink.vue'
|
||||||
import Filter from '../icons/Filter.vue'
|
import Filter from '../icons/Filter.vue'
|
||||||
import Close from '../icons/Close.vue'
|
import Close from '../icons/Close.vue'
|
||||||
import { typesBgColor, typesColor } from '../../consts/support_redis_type.js'
|
import { typesBgColor, typesColor } from '../../consts/support_redis_type.js'
|
||||||
|
import useTabStore from '../../stores/tab.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -30,10 +31,22 @@ const i18n = useI18n()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const loadingConnections = ref(false)
|
const loadingConnections = ref(false)
|
||||||
const expandedKeys = ref([props.server])
|
const expandedKeys = ref([props.server])
|
||||||
const selectedKeys = ref([props.server])
|
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
|
const tabStore = useTabStore()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {ComputedRef<string[]>}
|
||||||
|
*/
|
||||||
|
const selectedKeys = computed(() => {
|
||||||
|
const tab = find(tabStore.tabList, { name: props.server })
|
||||||
|
if (tab != null) {
|
||||||
|
return get(tab, 'selectedKeys', [props.server])
|
||||||
|
}
|
||||||
|
return [props.server]
|
||||||
|
})
|
||||||
|
|
||||||
const data = computed(() => {
|
const data = computed(() => {
|
||||||
const dbs = get(connectionStore.databases, props.server, [])
|
const dbs = get(connectionStore.databases, props.server, [])
|
||||||
return [
|
return [
|
||||||
|
@ -233,7 +246,7 @@ const onUpdateSelectedKeys = (keys, options) => {
|
||||||
connectionStore.loadKeyValue(props.server, 0)
|
connectionStore.loadKeyValue(props.server, 0)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
selectedKeys.value = keys
|
tabStore.setSelectedKeys(props.server, keys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +258,7 @@ const renderPrefix = ({ option }) => {
|
||||||
{ size: 20 },
|
{ size: 20 },
|
||||||
{
|
{
|
||||||
default: () => h(ToggleServer, { modelValue: false }),
|
default: () => h(ToggleServer, { modelValue: false }),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
case ConnectionType.RedisDB:
|
case ConnectionType.RedisDB:
|
||||||
return h(
|
return h(
|
||||||
|
@ -253,7 +266,7 @@ const renderPrefix = ({ option }) => {
|
||||||
{ size: 20 },
|
{ size: 20 },
|
||||||
{
|
{
|
||||||
default: () => h(ToggleDb, { modelValue: option.opened === true }),
|
default: () => h(ToggleDb, { modelValue: option.opened === true }),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
case ConnectionType.RedisKey:
|
case ConnectionType.RedisKey:
|
||||||
return h(
|
return h(
|
||||||
|
@ -261,7 +274,7 @@ const renderPrefix = ({ option }) => {
|
||||||
{ size: 20 },
|
{ size: 20 },
|
||||||
{
|
{
|
||||||
default: () => h(Layer),
|
default: () => h(Layer),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
case ConnectionType.RedisValue:
|
case ConnectionType.RedisValue:
|
||||||
return h(
|
return h(
|
||||||
|
@ -269,7 +282,7 @@ const renderPrefix = ({ option }) => {
|
||||||
{ size: 20 },
|
{ size: 20 },
|
||||||
{
|
{
|
||||||
default: () => h(Key),
|
default: () => h(Key),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,8 +322,8 @@ const renderSuffix = ({ option }) => {
|
||||||
connectionStore.reopenDatabase(server, db)
|
connectionStore.reopenDatabase(server, db)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ default: () => typeFilter }
|
{ default: () => typeFilter },
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// match pattern tag
|
// match pattern tag
|
||||||
|
@ -328,8 +341,8 @@ const renderSuffix = ({ option }) => {
|
||||||
connectionStore.reopenDatabase(server, db)
|
connectionStore.reopenDatabase(server, db)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ default: () => matchPattern }
|
{ default: () => matchPattern },
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (filterNodes.length > 0) {
|
if (filterNodes.length > 0) {
|
||||||
|
@ -362,7 +375,7 @@ const nodeProps = ({ option }) => {
|
||||||
contextMenuParam.x = e.clientX
|
contextMenuParam.x = e.clientX
|
||||||
contextMenuParam.y = e.clientY
|
contextMenuParam.y = e.clientY
|
||||||
contextMenuParam.show = true
|
contextMenuParam.show = true
|
||||||
selectedKeys.value = [option.key]
|
tabStore.setSelectedKeys(props.server, option.key)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// onMouseover() {
|
// onMouseover() {
|
||||||
|
|
|
@ -525,6 +525,9 @@ const useConnectionStore = defineStore('connections', {
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
// key not exists, remove this key
|
||||||
|
await this.deleteKey(server, db, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,7 +801,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @param {string} keyType
|
* @param {string} keyType
|
||||||
* @param {any} value
|
* @param {any} value
|
||||||
* @param {number} ttl
|
* @param {number} ttl
|
||||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
* @returns {Promise<{[msg]: string, success: boolean, [nodeKey]: {string}}>}
|
||||||
*/
|
*/
|
||||||
async setKey(connName, db, key, keyType, value, ttl) {
|
async setKey(connName, db, key, keyType, value, ttl) {
|
||||||
try {
|
try {
|
||||||
|
@ -809,7 +812,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
if (newKey > 0) {
|
if (newKey > 0) {
|
||||||
this._tidyNode(connName, db, key)
|
this._tidyNode(connName, db, key)
|
||||||
}
|
}
|
||||||
return { success }
|
return { success, nodeKey: `${connName}/db${db}#${ConnectionType.RedisValue}/${key}` }
|
||||||
} else {
|
} else {
|
||||||
return { success, msg }
|
return { success, msg }
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ const useTabStore = defineStore('tab', {
|
||||||
* @property {string} name connection name
|
* @property {string} name connection name
|
||||||
* @property {boolean} blank is blank tab
|
* @property {boolean} blank is blank tab
|
||||||
* @property {string} [title] tab title
|
* @property {string} [title] tab title
|
||||||
* @property {string} [icon] tab title
|
* @property {string} [icon] tab icon
|
||||||
|
* @property {string[]} selectedKeys
|
||||||
* @property {string} [type] key type
|
* @property {string} [type] key type
|
||||||
* @property {Object|Array} [value] key value
|
* @property {Object|Array} [value] key value
|
||||||
* @property {string} [server] server name
|
* @property {string} [server] server name
|
||||||
|
@ -51,20 +52,13 @@ const useTabStore = defineStore('tab', {
|
||||||
// }
|
// }
|
||||||
// return current
|
// return current
|
||||||
},
|
},
|
||||||
|
|
||||||
|
currentSelectedKeys() {
|
||||||
|
const tab = this.currentTab()
|
||||||
|
return get(tab, 'selectedKeys', [])
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
/**
|
|
||||||
* create new blank tab to tail
|
|
||||||
*/
|
|
||||||
newBlankTab() {
|
|
||||||
this.tabList.push({
|
|
||||||
name: Date.now().toString(),
|
|
||||||
title: 'new tab',
|
|
||||||
blank: true,
|
|
||||||
})
|
|
||||||
this._setActivatedIndex(size(this.tabList) - 1)
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param idx
|
* @param idx
|
||||||
|
@ -208,6 +202,21 @@ const useTabStore = defineStore('tab', {
|
||||||
this.tabList = []
|
this.tabList = []
|
||||||
this._setActivatedIndex(-1, false)
|
this._setActivatedIndex(-1, false)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} server
|
||||||
|
* @param {string|string[]} keys
|
||||||
|
*/
|
||||||
|
setSelectedKeys(server, keys) {
|
||||||
|
if (typeof keys === 'string') {
|
||||||
|
keys = [keys]
|
||||||
|
}
|
||||||
|
let tab = find(this.tabList, { name: server })
|
||||||
|
if (tab != null) {
|
||||||
|
tab.selectedKeys = keys
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue