feat: add partial entries loading for complex type(list/hash/set/zset/stream) #70
refactor: split "key value loading" into "key summary loading" and "key detail loading"
This commit is contained in:
parent
f2ebd7f358
commit
9618990de8
|
@ -29,11 +29,21 @@ type slowLogItem struct {
|
|||
Cost int64 `json:"cost"`
|
||||
}
|
||||
|
||||
type entryCursor struct {
|
||||
DB int
|
||||
Type string
|
||||
Key string
|
||||
Pattern string
|
||||
Cursor uint64
|
||||
XLast string // last stream pos
|
||||
}
|
||||
|
||||
type connectionItem struct {
|
||||
client redis.UniversalClient
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
cursor map[int]uint64 // current cursor of databases
|
||||
entryCursor map[int]entryCursor // current entry cursor of databases
|
||||
stepSize int64
|
||||
}
|
||||
|
||||
|
@ -265,6 +275,7 @@ func (b *browserService) getRedisClient(connName string, db int) (item connectio
|
|||
ctx: ctx,
|
||||
cancelFunc: cancelFunc,
|
||||
cursor: map[int]uint64{},
|
||||
entryCursor: map[int]entryCursor{},
|
||||
stepSize: int64(selConn.LoadSize),
|
||||
}
|
||||
if item.stepSize <= 0 {
|
||||
|
@ -487,6 +498,345 @@ func (b *browserService) LoadAllKeys(connName string, db int, match, keyType str
|
|||
return
|
||||
}
|
||||
|
||||
// GetKeySummary get key summary info
|
||||
func (b *browserService) GetKeySummary(param types.KeySummaryParam) (resp types.JSResp) {
|
||||
item, err := b.getRedisClient(param.Server, param.DB)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
client, ctx := item.client, item.ctx
|
||||
key := strutil.DecodeRedisKey(param.Key)
|
||||
var keyType string
|
||||
var dur time.Duration
|
||||
keyType, err = client.Type(ctx, key).Result()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if keyType == "none" {
|
||||
resp.Msg = "key not exists"
|
||||
return
|
||||
}
|
||||
|
||||
var data types.KeySummary
|
||||
data.Type = strings.ToLower(keyType)
|
||||
if dur, err = client.TTL(ctx, key).Result(); err != nil {
|
||||
data.TTL = -1
|
||||
} else {
|
||||
if dur < 0 {
|
||||
data.TTL = -1
|
||||
} else {
|
||||
data.TTL = int64(dur.Seconds())
|
||||
}
|
||||
}
|
||||
|
||||
data.Size, _ = client.MemoryUsage(ctx, key, 0).Result()
|
||||
switch data.Type {
|
||||
case "string":
|
||||
data.Length, _ = client.StrLen(ctx, key).Result()
|
||||
case "list":
|
||||
data.Length, _ = client.LLen(ctx, key).Result()
|
||||
case "hash":
|
||||
data.Length, _ = client.HLen(ctx, key).Result()
|
||||
case "set":
|
||||
data.Length, _ = client.SCard(ctx, key).Result()
|
||||
case "zset":
|
||||
data.Length, _ = client.ZCard(ctx, key).Result()
|
||||
case "stream":
|
||||
data.Length, _ = client.XLen(ctx, key).Result()
|
||||
default:
|
||||
resp.Msg = "unknown key type"
|
||||
return
|
||||
}
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = data
|
||||
return
|
||||
}
|
||||
|
||||
// GetKeyDetail get key detail
|
||||
func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JSResp) {
|
||||
item, err := b.getRedisClient(param.Server, param.DB)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
client, ctx, entryCors := item.client, item.ctx, item.entryCursor
|
||||
key := strutil.DecodeRedisKey(param.Key)
|
||||
var keyType string
|
||||
keyType, err = client.Type(ctx, key).Result()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if keyType == "none" {
|
||||
resp.Msg = "key not exists"
|
||||
return
|
||||
}
|
||||
|
||||
var data types.KeyDetail
|
||||
//var cursor uint64
|
||||
matchPattern := param.MatchPattern
|
||||
if len(matchPattern) <= 0 {
|
||||
matchPattern = "*"
|
||||
}
|
||||
|
||||
// define get entry cursor function
|
||||
getEntryCursor := func() (uint64, string) {
|
||||
if entry, ok := entryCors[param.DB]; !ok || entry.Key != key || entry.Pattern != matchPattern {
|
||||
// not the same key or match pattern, reset cursor
|
||||
entry = entryCursor{
|
||||
DB: param.DB,
|
||||
Key: key,
|
||||
Pattern: matchPattern,
|
||||
Cursor: 0,
|
||||
}
|
||||
entryCors[param.DB] = entry
|
||||
return 0, ""
|
||||
} else {
|
||||
return entry.Cursor, entry.XLast
|
||||
}
|
||||
}
|
||||
// define set entry cursor function
|
||||
setEntryCursor := func(cursor uint64) {
|
||||
entryCors[param.DB] = entryCursor{
|
||||
DB: param.DB,
|
||||
Type: "",
|
||||
Key: key,
|
||||
Pattern: matchPattern,
|
||||
Cursor: cursor,
|
||||
}
|
||||
}
|
||||
// define set last stream pos function
|
||||
setEntryXLast := func(last string) {
|
||||
entryCors[param.DB] = entryCursor{
|
||||
DB: param.DB,
|
||||
Type: "",
|
||||
Key: key,
|
||||
Pattern: matchPattern,
|
||||
XLast: last,
|
||||
}
|
||||
}
|
||||
|
||||
switch strings.ToLower(keyType) {
|
||||
case "string":
|
||||
var str string
|
||||
str, err = client.Get(ctx, key).Result()
|
||||
data.Value, data.DecodeType, data.ViewAs = strutil.ConvertTo(str, param.DecodeType, param.ViewAs)
|
||||
|
||||
case "list":
|
||||
loadListHandle := func() ([]string, bool, error) {
|
||||
var items []string
|
||||
var cursor uint64
|
||||
if param.Full {
|
||||
// load all
|
||||
cursor = 0
|
||||
items, err = client.LRange(ctx, key, 0, -1).Result()
|
||||
} else {
|
||||
cursor, _ = getEntryCursor()
|
||||
scanSize := int64(Preferences().GetScanSize())
|
||||
items, err = client.LRange(ctx, key, int64(cursor), int64(cursor)+scanSize-1).Result()
|
||||
cursor = cursor + uint64(scanSize)
|
||||
if len(items) < int(scanSize) {
|
||||
cursor = 0
|
||||
}
|
||||
}
|
||||
setEntryCursor(cursor)
|
||||
if err != nil {
|
||||
return items, false, err
|
||||
}
|
||||
return items, cursor == 0, nil
|
||||
}
|
||||
|
||||
data.Value, data.End, err = loadListHandle()
|
||||
|
||||
case "hash":
|
||||
loadHashHandle := func() (map[string]string, bool, error) {
|
||||
items := map[string]string{}
|
||||
scanSize := int64(Preferences().GetScanSize())
|
||||
var loadedVal []string
|
||||
var cursor uint64
|
||||
if param.Full {
|
||||
// load all
|
||||
cursor = 0
|
||||
for {
|
||||
loadedVal, cursor, err = client.HScan(ctx, key, cursor, "*", scanSize).Result()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
for i := 0; i < len(loadedVal); i += 2 {
|
||||
items[loadedVal[i]] = loadedVal[i+1]
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cursor, _ = getEntryCursor()
|
||||
loadedVal, cursor, err = client.HScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
for i := 0; i < len(loadedVal); i += 2 {
|
||||
items[loadedVal[i]] = loadedVal[i+1]
|
||||
}
|
||||
}
|
||||
setEntryCursor(cursor)
|
||||
return items, cursor == 0, nil
|
||||
}
|
||||
|
||||
data.Value, data.End, err = loadHashHandle()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
case "set":
|
||||
loadSetHandle := func() ([]string, bool, error) {
|
||||
var items []string
|
||||
var cursor uint64
|
||||
scanSize := int64(Preferences().GetScanSize())
|
||||
var loadedKey []string
|
||||
if param.Full {
|
||||
// load all
|
||||
cursor = 0
|
||||
for {
|
||||
loadedKey, cursor, err = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
||||
if err != nil {
|
||||
return items, false, err
|
||||
}
|
||||
items = append(items, loadedKey...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cursor, _ = getEntryCursor()
|
||||
loadedKey, cursor, err = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
||||
items = append(items, loadedKey...)
|
||||
}
|
||||
setEntryCursor(cursor)
|
||||
return items, cursor == 0, nil
|
||||
}
|
||||
|
||||
data.Value, data.End, err = loadSetHandle()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
case "zset":
|
||||
loadZSetHandle := func() ([]types.ZSetItem, bool, error) {
|
||||
var items []types.ZSetItem
|
||||
var cursor uint64
|
||||
scanSize := int64(Preferences().GetScanSize())
|
||||
var loadedVal []string
|
||||
if param.Full {
|
||||
// load all
|
||||
cursor = 0
|
||||
for {
|
||||
loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
||||
if err != nil {
|
||||
return items, false, err
|
||||
}
|
||||
var score float64
|
||||
for i := 0; i < len(loadedVal); i += 2 {
|
||||
if score, err = strconv.ParseFloat(loadedVal[i+1], 64); err == nil {
|
||||
items = append(items, types.ZSetItem{
|
||||
Value: loadedVal[i],
|
||||
Score: score,
|
||||
})
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cursor, _ = getEntryCursor()
|
||||
loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
||||
var score float64
|
||||
for i := 0; i < len(loadedVal); i += 2 {
|
||||
if score, err = strconv.ParseFloat(loadedVal[i+1], 64); err == nil {
|
||||
items = append(items, types.ZSetItem{
|
||||
Value: loadedVal[i],
|
||||
Score: score,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
setEntryCursor(cursor)
|
||||
return items, cursor == 0, nil
|
||||
}
|
||||
|
||||
data.Value, data.End, err = loadZSetHandle()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
case "stream":
|
||||
loadStreamHandle := func() ([]types.StreamItem, bool, error) {
|
||||
var msgs []redis.XMessage
|
||||
var items []types.StreamItem
|
||||
var last string
|
||||
if param.Full {
|
||||
// load all
|
||||
last = ""
|
||||
msgs, err = client.XRevRange(ctx, key, "+", "-").Result()
|
||||
} else {
|
||||
scanSize := int64(Preferences().GetScanSize())
|
||||
_, last = getEntryCursor()
|
||||
if len(last) <= 0 {
|
||||
last = "+"
|
||||
}
|
||||
if last != "+" {
|
||||
// add 1 more item when continue scan
|
||||
msgs, err = client.XRevRangeN(ctx, key, last, "-", scanSize+1).Result()
|
||||
msgs = msgs[1:]
|
||||
} else {
|
||||
msgs, err = client.XRevRangeN(ctx, key, last, "-", scanSize).Result()
|
||||
}
|
||||
scanCount := len(msgs)
|
||||
if scanCount <= 0 || scanCount < int(scanSize) {
|
||||
last = ""
|
||||
} else if scanCount > 0 {
|
||||
last = msgs[scanCount-1].ID
|
||||
}
|
||||
}
|
||||
setEntryXLast(last)
|
||||
for _, msg := range msgs {
|
||||
items = append(items, types.StreamItem{
|
||||
ID: msg.ID,
|
||||
Value: msg.Values,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return items, false, err
|
||||
}
|
||||
return items, last == "", nil
|
||||
}
|
||||
|
||||
data.Value, data.End, err = loadStreamHandle()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
resp.Success = true
|
||||
resp.Data = data
|
||||
return
|
||||
}
|
||||
|
||||
// GetKeyValue get value by key
|
||||
func (b *browserService) GetKeyValue(connName string, db int, k any, viewAs, decodeType string) (resp types.JSResp) {
|
||||
item, err := b.getRedisClient(connName, db)
|
||||
|
@ -774,6 +1124,7 @@ func (b *browserService) SetHashValue(connName string, db int, k any, field, new
|
|||
key := strutil.DecodeRedisKey(k)
|
||||
var removedField []string
|
||||
updatedField := map[string]string{}
|
||||
replacedField := map[string]string{}
|
||||
if len(field) <= 0 {
|
||||
// old filed is empty, add new field
|
||||
_, err = client.HSet(ctx, key, newField, value).Result()
|
||||
|
@ -795,6 +1146,7 @@ func (b *browserService) SetHashValue(connName string, db int, k any, field, new
|
|||
_, err = client.HSet(ctx, key, newField, value).Result()
|
||||
removedField = append(removedField, field)
|
||||
updatedField[newField] = value
|
||||
replacedField[field] = newField
|
||||
}
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
|
@ -805,6 +1157,7 @@ func (b *browserService) SetHashValue(connName string, db int, k any, field, new
|
|||
resp.Data = map[string]any{
|
||||
"removed": removedField,
|
||||
"updated": updatedField,
|
||||
"replaced": replacedField,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -944,10 +1297,11 @@ func (b *browserService) SetSetItem(connName string, db int, k any, remove bool,
|
|||
|
||||
client, ctx := item.client, item.ctx
|
||||
key := strutil.DecodeRedisKey(k)
|
||||
var affected int64
|
||||
if remove {
|
||||
_, err = client.SRem(ctx, key, members...).Result()
|
||||
affected, err = client.SRem(ctx, key, members...).Result()
|
||||
} else {
|
||||
_, err = client.SAdd(ctx, key, members...).Result()
|
||||
affected, err = client.SAdd(ctx, key, members...).Result()
|
||||
}
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
|
@ -955,6 +1309,9 @@ func (b *browserService) SetSetItem(connName string, db int, k any, remove bool,
|
|||
}
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = map[string]any{
|
||||
"affected": affected,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1003,6 +1360,9 @@ func (b *browserService) UpdateZSetValue(connName string, db int, k any, value,
|
|||
Score: score,
|
||||
Member: value,
|
||||
}).Result()
|
||||
if err == nil {
|
||||
updated[value] = score
|
||||
}
|
||||
} else {
|
||||
// remove old value and add new one
|
||||
_, err = client.ZRem(ctx, key, value).Result()
|
||||
|
@ -1075,7 +1435,8 @@ func (b *browserService) AddStreamValue(connName string, db int, k any, ID strin
|
|||
|
||||
client, ctx := item.client, item.ctx
|
||||
key := strutil.DecodeRedisKey(k)
|
||||
_, err = client.XAdd(ctx, &redis.XAddArgs{
|
||||
var updateID string
|
||||
updateID, err = client.XAdd(ctx, &redis.XAddArgs{
|
||||
Stream: key,
|
||||
ID: ID,
|
||||
Values: fieldItems,
|
||||
|
@ -1086,6 +1447,9 @@ func (b *browserService) AddStreamValue(connName string, db int, k any, ID strin
|
|||
}
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = map[string]any{
|
||||
"updateID": updateID,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1099,8 +1463,18 @@ func (b *browserService) RemoveStreamValues(connName string, db int, k any, IDs
|
|||
|
||||
client, ctx := item.client, item.ctx
|
||||
key := strutil.DecodeRedisKey(k)
|
||||
_, err = client.XDel(ctx, key, IDs...).Result()
|
||||
|
||||
var affected int64
|
||||
affected, err = client.XDel(ctx, key, IDs...).Result()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = map[string]any{
|
||||
"affected": affected,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,3 +5,35 @@ type JSResp struct {
|
|||
Msg string `json:"msg"`
|
||||
Data any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type KeySummaryParam struct {
|
||||
Server string `json:"server"`
|
||||
DB int `json:"db"`
|
||||
Key any `json:"key"`
|
||||
}
|
||||
|
||||
type KeySummary struct {
|
||||
Type string `json:"type"`
|
||||
TTL int64 `json:"ttl"`
|
||||
Size int64 `json:"size"`
|
||||
Length int64 `json:"length"`
|
||||
}
|
||||
|
||||
type KeyDetailParam struct {
|
||||
Server string `json:"server"`
|
||||
DB int `json:"db"`
|
||||
Key any `json:"key"`
|
||||
ViewAs string `json:"viewAs,omitempty"`
|
||||
DecodeType string `json:"decodeType,omitempty"`
|
||||
MatchPattern string `json:"matchPattern,omitempty"`
|
||||
Reset bool `json:"reset"`
|
||||
Full bool `json:"full"`
|
||||
}
|
||||
|
||||
type KeyDetail struct {
|
||||
Value any `json:"value"`
|
||||
Length int64 `json:"length,omitempty"`
|
||||
ViewAs string `json:"viewAs,omitempty"`
|
||||
DecodeType string `json:"decodeType,omitempty"`
|
||||
End bool `json:"end"`
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ const props = defineProps({
|
|||
|
||||
const data = reactive({
|
||||
navMenuWidth: 60,
|
||||
toolbarHeight: 45,
|
||||
toolbarHeight: 38,
|
||||
})
|
||||
|
||||
const tabStore = useTabStore()
|
||||
|
|
|
@ -56,11 +56,13 @@ const tabContent = computed(() => {
|
|||
length: tab.length || 0,
|
||||
viewAs: tab.viewAs,
|
||||
decode: tab.decode,
|
||||
end: tab.end,
|
||||
loading: tab.loading === true,
|
||||
}
|
||||
})
|
||||
|
||||
const isBlankValue = computed(() => {
|
||||
return tabContent.value.value == null
|
||||
return tabContent.value?.keyPath == null
|
||||
})
|
||||
|
||||
const selectedSubTab = computed(() => {
|
||||
|
@ -133,19 +135,7 @@ watch(
|
|||
<span>{{ $t('interface.sub_tab.key_detail') }}</span>
|
||||
</n-space>
|
||||
</template>
|
||||
<content-value-wrapper
|
||||
:blank="isBlankValue"
|
||||
:db="tabContent.db"
|
||||
:decode="tabContent.decode"
|
||||
:key-code="tabContent.keyCode"
|
||||
:key-path="tabContent.keyPath"
|
||||
:length="tabContent.length"
|
||||
:name="tabContent.name"
|
||||
:size="tabContent.size"
|
||||
:ttl="tabContent.ttl"
|
||||
:type="tabContent.type"
|
||||
:value="tabContent.value"
|
||||
:view-as="tabContent.viewAs" />
|
||||
<content-value-wrapper :blank="isBlankValue" :content="tabContent" />
|
||||
</n-tab-pane>
|
||||
|
||||
<!-- cli pane -->
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<script setup>
|
||||
import { h, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||
import Refresh from '@/components/icons/Refresh.vue'
|
||||
import { debounce, isEmpty, map, size, split } from 'lodash'
|
||||
import { debounce, isEmpty, map, size } from 'lodash'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import dayjs from 'dayjs'
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@ import IconButton from '@/components/common/IconButton.vue'
|
|||
import Copy from '@/components/icons/Copy.vue'
|
||||
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
||||
import { computed } from 'vue'
|
||||
import { isEmpty, padStart } from 'lodash'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import { padStart } from 'lodash'
|
||||
|
||||
const props = defineProps({
|
||||
server: String,
|
||||
|
@ -34,32 +32,18 @@ const props = defineProps({
|
|||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
viewAs: {
|
||||
type: String,
|
||||
default: formatTypes.PLAIN_TEXT,
|
||||
},
|
||||
decode: {
|
||||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['reload', 'rename', 'delete'])
|
||||
|
||||
const dialogStore = useDialog()
|
||||
const browserStore = useBrowserStore()
|
||||
const i18n = useI18n()
|
||||
|
||||
const binaryKey = computed(() => {
|
||||
return !!props.keyCode
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
*/
|
||||
const keyName = computed(() => {
|
||||
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
||||
})
|
||||
|
||||
const ttlString = computed(() => {
|
||||
let s = ''
|
||||
if (props.ttl > 0) {
|
||||
|
@ -77,10 +61,6 @@ const ttlString = computed(() => {
|
|||
return s
|
||||
})
|
||||
|
||||
const onReloadKey = () => {
|
||||
browserStore.loadKeyValue(props.server, props.db, keyName.value, props.viewAs, props.decode)
|
||||
}
|
||||
|
||||
const onCopyKey = () => {
|
||||
ClipboardSetText(props.keyPath)
|
||||
.then((succ) => {
|
||||
|
@ -92,33 +72,20 @@ const onCopyKey = () => {
|
|||
$message.error(e.message)
|
||||
})
|
||||
}
|
||||
|
||||
const onRenameKey = () => {
|
||||
if (binaryKey.value) {
|
||||
$message.error(i18n.t('dialogue.rename_binary_key_fail'))
|
||||
} else {
|
||||
dialogStore.openRenameKeyDialog(props.server, props.db, props.keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
const onDeleteKey = () => {
|
||||
$dialog.warning(i18n.t('dialogue.remove_tip', { name: props.keyPath }), () => {
|
||||
browserStore.deleteKey(props.server, props.db, keyName.value).then((success) => {
|
||||
if (success) {
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: props.keyPath }))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content-toolbar flex-box-h">
|
||||
<n-input-group>
|
||||
<redis-type-tag :binary-key="binaryKey" :type="props.keyType" size="large" />
|
||||
<n-input v-model:value="props.keyPath">
|
||||
<n-input v-model:value="props.keyPath" readonly>
|
||||
<template #suffix>
|
||||
<icon-button :icon="Refresh" size="18" t-tooltip="interface.reload" @click="onReloadKey" />
|
||||
<icon-button
|
||||
:icon="Refresh"
|
||||
:loading="props.loading"
|
||||
size="18"
|
||||
t-tooltip="interface.reload"
|
||||
@click="emit('reload')" />
|
||||
</template>
|
||||
</n-input>
|
||||
<icon-button :icon="Copy" border size="18" t-tooltip="interface.copy_key" @click="onCopyKey" />
|
||||
|
@ -135,11 +102,11 @@ const onDeleteKey = () => {
|
|||
</template>
|
||||
TTL{{ `${ttl > 0 ? ': ' + ttl + $t('common.second') : ''}` }}
|
||||
</n-tooltip>
|
||||
<icon-button :icon="Edit" border size="18" t-tooltip="interface.rename_key" @click="onRenameKey" />
|
||||
<icon-button :icon="Edit" border size="18" t-tooltip="interface.rename_key" @click="emit('rename')" />
|
||||
</n-button-group>
|
||||
<n-tooltip :show-arrow="false">
|
||||
<template #trigger>
|
||||
<n-button :focusable="false" @click="onDeleteKey">
|
||||
<n-button :focusable="false" @click="emit('delete')">
|
||||
<template #icon>
|
||||
<n-icon :component="Delete" size="18" />
|
||||
</template>
|
||||
|
|
|
@ -7,10 +7,13 @@ import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
|||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||
import useDialogStore from 'stores/dialog.js'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { isEmpty, size } from 'lodash'
|
||||
import bytes from 'bytes'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import LoadList from '@/components/icons/LoadList.vue'
|
||||
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||
import IconButton from '@/components/common/IconButton.vue'
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
|
@ -38,8 +41,12 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
end: Boolean,
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
|
@ -149,14 +156,7 @@ const actionColumn = {
|
|||
row.key,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.key }))
|
||||
// update display value
|
||||
// if (!isEmpty(removed)) {
|
||||
// for (const elem of removed) {
|
||||
// delete props.value[elem]
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -175,14 +175,7 @@ const actionColumn = {
|
|||
currentEditRow.value.value,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.save_value_succ'))
|
||||
// update display value
|
||||
// if (!isEmpty(updated)) {
|
||||
// for (const key in updated) {
|
||||
// props.value[key] = updated[key]
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -223,6 +216,12 @@ const tableData = computed(() => {
|
|||
}
|
||||
return data
|
||||
})
|
||||
|
||||
const entries = computed(() => {
|
||||
const len = size(tableData.value)
|
||||
return `${len} / ${Math.max(len, props.length)}`
|
||||
})
|
||||
|
||||
const onAddRow = () => {
|
||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.HASH)
|
||||
}
|
||||
|
@ -268,14 +267,16 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
:view-as="props.viewAs"
|
||||
class="value-item-part" />
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<n-input-group>
|
||||
|
@ -294,6 +295,22 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
</n-input-group>
|
||||
</div>
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group>
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadList"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_more_entries"
|
||||
@click="emit('loadmore')" />
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadAll"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_all_entries"
|
||||
@click="emit('loadall')" />
|
||||
</n-button-group>
|
||||
<n-button :focusable="false" plain @click="onAddRow">
|
||||
<template #icon>
|
||||
<n-icon :component="AddLink" size="18" />
|
||||
|
@ -301,24 +318,25 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
{{ $t('interface.add_row') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="value-wrapper value-item-part fill-height flex-box-h">
|
||||
<div class="value-wrapper value-item-part flex-box-v flex-item-expand">
|
||||
<n-data-table
|
||||
:key="(row) => row.no"
|
||||
:bordered="false"
|
||||
:bottom-bordered="false"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="props.loading"
|
||||
:single-column="true"
|
||||
:single-line="false"
|
||||
class="flex-item-expand"
|
||||
flex-height
|
||||
max-height="100%"
|
||||
size="small"
|
||||
striped
|
||||
virtual-scroll
|
||||
@update:filters="onUpdateFilter" />
|
||||
</div>
|
||||
<div class="value-footer flex-box-h">
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ props.length }}</n-text>
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text>
|
||||
<n-divider v-if="!isNaN(props.length)" vertical />
|
||||
<n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text>
|
||||
<div class="flex-item-expand"></div>
|
||||
|
|
|
@ -11,6 +11,9 @@ import useDialogStore from 'stores/dialog.js'
|
|||
import bytes from 'bytes'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import LoadList from '@/components/icons/LoadList.vue'
|
||||
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||
import IconButton from '@/components/common/IconButton.vue'
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
|
@ -38,8 +41,12 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
end: Boolean,
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
|
@ -106,12 +113,7 @@ const actionColumn = {
|
|||
row.no - 1,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: '#' + row.no }))
|
||||
// update display value
|
||||
// if (!isEmpty(removed)) {
|
||||
// props.value.splice(removed[0], 1)
|
||||
// }
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -129,14 +131,7 @@ const actionColumn = {
|
|||
currentEditRow.value.value,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.save_value_succ'))
|
||||
// update display value
|
||||
// if (!isEmpty(updated)) {
|
||||
// for (const key in updated) {
|
||||
// props.value[key] = updated[key]
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -178,6 +173,11 @@ const tableData = computed(() => {
|
|||
return data
|
||||
})
|
||||
|
||||
const entries = computed(() => {
|
||||
const len = size(tableData.value)
|
||||
return `${len} / ${Math.max(len, props.length)}`
|
||||
})
|
||||
|
||||
const onAddValue = (value) => {
|
||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.LIST)
|
||||
}
|
||||
|
@ -200,14 +200,16 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
:view-as="props.viewAs"
|
||||
class="value-item-part" />
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<n-input
|
||||
|
@ -218,6 +220,22 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
@update:value="onFilterInput" />
|
||||
</div>
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group>
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadList"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_more_entries"
|
||||
@click="emit('loadmore')" />
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadAll"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_all_entries"
|
||||
@click="emit('loadall')" />
|
||||
</n-button-group>
|
||||
<n-button :focusable="false" plain @click="onAddValue">
|
||||
<template #icon>
|
||||
<n-icon :component="AddLink" size="18" />
|
||||
|
@ -225,24 +243,25 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
{{ $t('interface.add_row') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="value-wrapper value-item-part fill-height flex-box-h">
|
||||
<div class="value-wrapper value-item-part flex-box-v flex-item-expand">
|
||||
<n-data-table
|
||||
:key="(row) => row.no"
|
||||
:bordered="false"
|
||||
:bottom-bordered="false"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="props.loading"
|
||||
:single-column="true"
|
||||
:single-line="false"
|
||||
class="flex-item-expand"
|
||||
flex-height
|
||||
max-height="100%"
|
||||
size="small"
|
||||
striped
|
||||
virtual-scroll
|
||||
@update:filters="onUpdateFilter" />
|
||||
</div>
|
||||
<div class="value-footer flex-box-h">
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ props.length }}</n-text>
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text>
|
||||
<n-divider v-if="!isNaN(props.length)" vertical />
|
||||
<n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text>
|
||||
<div class="flex-item-expand"></div>
|
||||
|
|
|
@ -11,6 +11,9 @@ import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
|||
import bytes from 'bytes'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import LoadList from '@/components/icons/LoadList.vue'
|
||||
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||
import IconButton from '@/components/common/IconButton.vue'
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
|
@ -38,8 +41,12 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
end: Boolean,
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
|
@ -107,10 +114,7 @@ const actionColumn = {
|
|||
row.value,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.value }))
|
||||
// update display value
|
||||
// props.value.splice(row.no - 1, 1)
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -128,10 +132,7 @@ const actionColumn = {
|
|||
currentEditRow.value.value,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.save_value_succ'))
|
||||
// update display value
|
||||
// props.value[row.no - 1] = currentEditRow.value.value
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -173,6 +174,11 @@ const tableData = computed(() => {
|
|||
return data
|
||||
})
|
||||
|
||||
const entries = computed(() => {
|
||||
const len = size(tableData.value)
|
||||
return `${len} / ${Math.max(len, props.length)}`
|
||||
})
|
||||
|
||||
const onAddValue = (value) => {
|
||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.SET)
|
||||
}
|
||||
|
@ -195,14 +201,16 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
:view-as="props.viewAs"
|
||||
class="value-item-part" />
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<n-input
|
||||
|
@ -213,6 +221,22 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
@update:value="onFilterInput" />
|
||||
</div>
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group>
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadList"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_more_entries"
|
||||
@click="emit('loadmore')" />
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadAll"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_all_entries"
|
||||
@click="emit('loadall')" />
|
||||
</n-button-group>
|
||||
<n-button :focusable="false" plain @click="onAddValue">
|
||||
<template #icon>
|
||||
<n-icon :component="AddLink" size="18" />
|
||||
|
@ -220,24 +244,25 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
{{ $t('interface.add_row') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="value-wrapper value-item-part fill-height flex-box-h">
|
||||
<div class="value-wrapper value-item-part flex-box-v flex-item-expand">
|
||||
<n-data-table
|
||||
:key="(row) => row.no"
|
||||
:bordered="false"
|
||||
:bottom-bordered="false"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="props.loading"
|
||||
:single-column="true"
|
||||
:single-line="false"
|
||||
class="flex-item-expand"
|
||||
flex-height
|
||||
max-height="100%"
|
||||
size="small"
|
||||
striped
|
||||
virtual-scroll
|
||||
@update:filters="onUpdateFilter" />
|
||||
</div>
|
||||
<div class="value-footer flex-box-h">
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ props.length }}</n-text>
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text>
|
||||
<n-divider v-if="!isNaN(props.length)" vertical />
|
||||
<n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text>
|
||||
<div class="flex-item-expand"></div>
|
||||
|
|
|
@ -7,10 +7,13 @@ import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
|||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||
import useDialogStore from 'stores/dialog.js'
|
||||
import { includes, isEmpty, keys, some, values } from 'lodash'
|
||||
import { includes, isEmpty, keys, size, some, values } from 'lodash'
|
||||
import bytes from 'bytes'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import LoadList from '@/components/icons/LoadList.vue'
|
||||
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||
import IconButton from '@/components/common/IconButton.vue'
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
|
@ -27,7 +30,10 @@ const props = defineProps({
|
|||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
value: Object,
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
size: Number,
|
||||
length: Number,
|
||||
viewAs: {
|
||||
|
@ -38,8 +44,12 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
end: Boolean,
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
|
@ -115,14 +125,7 @@ const actionColumn = {
|
|||
row.id,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.id }))
|
||||
// update display value
|
||||
// if (!isEmpty(removed)) {
|
||||
// for (const elem of removed) {
|
||||
// delete props.value[elem]
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
@ -137,15 +140,22 @@ const columns = reactive([idColumn, valueColumn, actionColumn])
|
|||
|
||||
const tableData = computed(() => {
|
||||
const data = []
|
||||
if (!isEmpty(props.value)) {
|
||||
for (const elem of props.value) {
|
||||
data.push({
|
||||
id: elem.id,
|
||||
value: elem.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
return data
|
||||
})
|
||||
|
||||
const entries = computed(() => {
|
||||
const len = size(tableData.value)
|
||||
return `${len} / ${Math.max(len, props.length)}`
|
||||
})
|
||||
|
||||
const onAddRow = () => {
|
||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.STREAM)
|
||||
}
|
||||
|
@ -180,14 +190,16 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
:view-as="props.viewAs"
|
||||
class="value-item-part" />
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<n-input-group>
|
||||
|
@ -206,6 +218,22 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
</n-input-group>
|
||||
</div>
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group>
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadList"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_more_entries"
|
||||
@click="emit('loadmore')" />
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadAll"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_all_entries"
|
||||
@click="emit('loadall')" />
|
||||
</n-button-group>
|
||||
<n-button :focusable="false" plain @click="onAddRow">
|
||||
<template #icon>
|
||||
<n-icon :component="AddLink" size="18" />
|
||||
|
@ -213,17 +241,18 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
{{ $t('interface.add_row') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="value-wrapper value-item-part fill-height flex-box-h">
|
||||
<div class="value-wrapper value-item-part flex-box-v flex-item-expand">
|
||||
<n-data-table
|
||||
:key="(row) => row.id"
|
||||
:bordered="false"
|
||||
:bottom-bordered="false"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="props.loading"
|
||||
:single-column="true"
|
||||
:single-line="false"
|
||||
class="flex-item-expand"
|
||||
flex-height
|
||||
max-height="100%"
|
||||
size="small"
|
||||
striped
|
||||
virtual-scroll
|
||||
|
@ -231,7 +260,7 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
</div>
|
||||
|
||||
<div class="value-footer flex-box-h">
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ props.length }}</n-text>
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text>
|
||||
<n-divider v-if="!isNaN(props.length)" vertical />
|
||||
<n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text>
|
||||
<div class="flex-item-expand"></div>
|
||||
|
|
|
@ -43,8 +43,11 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['reload', 'rename', 'delete'])
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
|
@ -53,16 +56,6 @@ const keyName = computed(() => {
|
|||
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
||||
})
|
||||
|
||||
// const viewOption = computed(() =>
|
||||
// map(types, (t) => {
|
||||
// return {
|
||||
// value: t,
|
||||
// label: t,
|
||||
// key: t,
|
||||
// }
|
||||
// }),
|
||||
// )
|
||||
|
||||
const keyType = redisTypes.STRING
|
||||
const viewLanguage = computed(() => {
|
||||
switch (props.viewAs) {
|
||||
|
@ -74,11 +67,23 @@ const viewLanguage = computed(() => {
|
|||
})
|
||||
|
||||
const onViewTypeUpdate = (viewType) => {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value, viewType, props.decode)
|
||||
browserStore.loadKeyDetail({
|
||||
server: props.name,
|
||||
db: props.db,
|
||||
key: keyName.value,
|
||||
viewType,
|
||||
decodeType: props.decode,
|
||||
})
|
||||
}
|
||||
|
||||
const onDecodeTypeUpdate = (decodeType) => {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value, props.viewAs, decodeType)
|
||||
browserStore.loadKeyDetail({
|
||||
server: props.name,
|
||||
db: props.db,
|
||||
key: keyName.value,
|
||||
viewType: props.viewAs,
|
||||
decodeType,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,7 +131,7 @@ const onSaveValue = async () => {
|
|||
props.decode,
|
||||
)
|
||||
if (success) {
|
||||
await browserStore.loadKeyValue(props.name, props.db, keyName.value)
|
||||
await browserStore.loadKeyDetail({ server: props.name, db: props.db, key: keyName.value })
|
||||
$message.success(i18n.t('dialogue.save_value_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -144,14 +149,16 @@ const onSaveValue = async () => {
|
|||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="keyCode"
|
||||
:key-path="keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
:view-as="props.viewAs"
|
||||
class="value-item-part" />
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group v-if="!inEdit">
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script setup>
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
import ContentValueString from '@/components/content_value/ContentValueString.vue'
|
||||
import ContentValueHash from '@/components/content_value/ContentValueHash.vue'
|
||||
|
@ -9,37 +8,48 @@ import ContentValueZset from '@/components/content_value/ContentValueZSet.vue'
|
|||
import ContentValueStream from '@/components/content_value/ContentValueStream.vue'
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useDialogStore from 'stores/dialog.js'
|
||||
|
||||
const themeVars = useThemeVars()
|
||||
const browserStore = useBrowserStore()
|
||||
const dialogStore = useDialogStore()
|
||||
|
||||
const props = defineProps({
|
||||
blank: Boolean,
|
||||
type: String,
|
||||
name: String,
|
||||
db: Number,
|
||||
keyPath: String,
|
||||
keyCode: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
ttl: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
value: [String, Object],
|
||||
size: Number,
|
||||
length: Number,
|
||||
viewAs: {
|
||||
type: String,
|
||||
default: formatTypes.PLAIN_TEXT,
|
||||
},
|
||||
decode: {
|
||||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
content: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<{
|
||||
* type:
|
||||
* String, name: String,
|
||||
* db: Number,
|
||||
* keyPath: String,
|
||||
* keyCode: Array,
|
||||
* ttl: Number,
|
||||
* value: [String, Object],
|
||||
* size: Number,
|
||||
* length: Number,
|
||||
* viewAs: String,
|
||||
* decode: String,
|
||||
* end: Boolean
|
||||
* }>}
|
||||
*/
|
||||
const data = computed(() => {
|
||||
return props.content
|
||||
})
|
||||
|
||||
const binaryKey = computed(() => {
|
||||
return !!data.value.keyCode
|
||||
})
|
||||
|
||||
const valueComponents = {
|
||||
[redisTypes.STRING]: ContentValueString,
|
||||
[redisTypes.HASH]: ContentValueHash,
|
||||
|
@ -49,34 +59,103 @@ const valueComponents = {
|
|||
[redisTypes.STREAM]: ContentValueStream,
|
||||
}
|
||||
|
||||
/**
|
||||
* reload current selection key
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
const onReloadKey = async () => {
|
||||
await browserStore.loadKeyValue(props.name, props.db, props.key, props.viewAs)
|
||||
const keyName = computed(() => {
|
||||
return !isEmpty(data.value.keyCode) ? data.value.keyCode : data.value.keyPath
|
||||
})
|
||||
|
||||
const loadData = async (reset, full) => {
|
||||
try {
|
||||
const { name, db, view, decodeType, matchPattern } = data.value
|
||||
await browserStore.loadKeyDetail({
|
||||
server: name,
|
||||
db: db,
|
||||
key: keyName.value,
|
||||
viewType: view,
|
||||
decodeType: decodeType,
|
||||
matchPattern: matchPattern,
|
||||
reset: reset === true,
|
||||
full: full === true,
|
||||
})
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
const onReload = async () => {
|
||||
try {
|
||||
const { name, db, keyCode, keyPath } = data.value
|
||||
await browserStore.reloadKey({ server: name, db, key: keyCode || keyPath })
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
const onRename = () => {
|
||||
const { name, db, keyPath } = data.value
|
||||
if (binaryKey.value) {
|
||||
$message.error(i18n.t('dialogue.rename_binary_key_fail'))
|
||||
} else {
|
||||
dialogStore.openRenameKeyDialog(name, db, keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
$dialog.warning(i18n.t('dialogue.remove_tip', { name: props.keyPath }), () => {
|
||||
const { name, db } = data.value
|
||||
browserStore.deleteKey(name, db, keyName.value).then((success) => {
|
||||
if (success) {
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: props.keyPath }))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const onLoadMore = () => {
|
||||
loadData(false, false)
|
||||
}
|
||||
|
||||
const onLoadAll = () => {
|
||||
loadData(false, true)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// onReload()
|
||||
loadData(false, false)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => data.value?.keyPath,
|
||||
() => {
|
||||
// onReload()
|
||||
loadData(false, false)
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-empty v-if="props.blank" :description="$t('interface.nonexist_tab_content')" class="empty-content">
|
||||
<template #extra>
|
||||
<n-button :focusable="false" @click="onReloadKey">{{ $t('interface.reload') }}</n-button>
|
||||
<n-button :focusable="false" @click="onReload">{{ $t('interface.reload') }}</n-button>
|
||||
</template>
|
||||
</n-empty>
|
||||
<keep-alive v-else>
|
||||
<component
|
||||
:is="valueComponents[props.type]"
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:length="props.length"
|
||||
:name="props.name"
|
||||
:size="props.size"
|
||||
:ttl="props.ttl"
|
||||
:value="props.value"
|
||||
:view-as="props.viewAs" />
|
||||
:is="valueComponents[data.type]"
|
||||
:db="data.db"
|
||||
:decode="data.decode || decodeTypes.NONE"
|
||||
:end="data.end"
|
||||
:key-code="data.keyCode"
|
||||
:key-path="data.keyPath"
|
||||
:length="data.length"
|
||||
:loading="data.loading === true"
|
||||
:name="data.name"
|
||||
:size="data.size"
|
||||
:ttl="data.ttl"
|
||||
:value="data.value"
|
||||
:view-as="data.viewAs || formatTypes.PLAIN_TEXT"
|
||||
@delete="onDelete"
|
||||
@loadall="onLoadAll"
|
||||
@loadmore="onLoadMore"
|
||||
@reload="onReload"
|
||||
@rename="onRename" />
|
||||
</keep-alive>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -6,11 +6,14 @@ import AddLink from '@/components/icons/AddLink.vue'
|
|||
import { NButton, NCode, NIcon, NInput, NInputNumber, useThemeVars } from 'naive-ui'
|
||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { isEmpty, size } from 'lodash'
|
||||
import useDialogStore from 'stores/dialog.js'
|
||||
import bytes from 'bytes'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import LoadList from '@/components/icons/LoadList.vue'
|
||||
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||
import IconButton from '@/components/common/IconButton.vue'
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
|
@ -38,8 +41,12 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: decodeTypes.NONE,
|
||||
},
|
||||
end: Boolean,
|
||||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ComputedRef<string|number[]>}
|
||||
|
@ -178,7 +185,6 @@ const actionColumn = {
|
|||
row.value,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.value }))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -203,7 +209,6 @@ const actionColumn = {
|
|||
currentEditRow.value.score,
|
||||
)
|
||||
if (success) {
|
||||
browserStore.loadKeyValue(props.name, props.db, keyName.value).then((r) => {})
|
||||
$message.success(i18n.t('dialogue.save_value_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -235,6 +240,7 @@ const columns = reactive([
|
|||
|
||||
const tableData = computed(() => {
|
||||
const data = []
|
||||
if (!isEmpty(props.value)) {
|
||||
let index = 0
|
||||
for (const elem of props.value) {
|
||||
data.push({
|
||||
|
@ -243,9 +249,15 @@ const tableData = computed(() => {
|
|||
score: elem.score,
|
||||
})
|
||||
}
|
||||
}
|
||||
return data
|
||||
})
|
||||
|
||||
const entries = computed(() => {
|
||||
const len = size(tableData.value)
|
||||
return `${len} / ${Math.max(len, props.length)}`
|
||||
})
|
||||
|
||||
const onAddRow = () => {
|
||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.ZSET)
|
||||
}
|
||||
|
@ -293,14 +305,16 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:decode="props.decode"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
:view-as="props.viewAs"
|
||||
class="value-item-part" />
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<n-input-group>
|
||||
|
@ -324,6 +338,22 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
</n-input-group>
|
||||
</div>
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group>
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadList"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_more_entries"
|
||||
@click="emit('loadmore')" />
|
||||
<icon-button
|
||||
:disabled="props.end || props.loading"
|
||||
:icon="LoadAll"
|
||||
border
|
||||
size="18"
|
||||
t-tooltip="interface.load_all_entries"
|
||||
@click="emit('loadall')" />
|
||||
</n-button-group>
|
||||
<n-button :focusable="false" plain @click="onAddRow">
|
||||
<template #icon>
|
||||
<n-icon :component="AddLink" size="18" />
|
||||
|
@ -331,24 +361,25 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
{{ $t('interface.add_row') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="value-wrapper value-item-part fill-height flex-box-h">
|
||||
<div class="value-wrapper value-item-part flex-box-v flex-item-expand">
|
||||
<n-data-table
|
||||
:key="(row) => row.no"
|
||||
:bordered="false"
|
||||
:bottom-bordered="false"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="props.loading"
|
||||
:single-column="true"
|
||||
:single-line="false"
|
||||
class="flex-item-expand"
|
||||
flex-height
|
||||
max-height="100%"
|
||||
size="small"
|
||||
striped
|
||||
virtual-scroll
|
||||
@update:filters="onUpdateFilter" />
|
||||
</div>
|
||||
<div class="value-footer flex-box-h">
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ props.length }}</n-text>
|
||||
<n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text>
|
||||
<n-divider v-if="!isNaN(props.length)" vertical />
|
||||
<n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text>
|
||||
<div class="flex-item-expand"></div>
|
||||
|
|
|
@ -11,6 +11,7 @@ import AddZSetValue from '@/components/new_value/AddZSetValue.vue'
|
|||
import NewStreamValue from '@/components/new_value/NewStreamValue.vue'
|
||||
import { isEmpty, size, slice } from 'lodash'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import useTabStore from 'stores/tab.js'
|
||||
|
||||
const i18n = useI18n()
|
||||
const newForm = reactive({
|
||||
|
@ -79,6 +80,7 @@ watch(
|
|||
)
|
||||
|
||||
const browserStore = useBrowserStore()
|
||||
const tab = useTabStore()
|
||||
const onAdd = async () => {
|
||||
try {
|
||||
const { server, db, key, keyCode, type } = newForm
|
||||
|
@ -87,6 +89,7 @@ const onAdd = async () => {
|
|||
value = defaultValue[type]
|
||||
}
|
||||
const keyName = isEmpty(keyCode) ? key : keyCode
|
||||
let updated = false
|
||||
switch (type) {
|
||||
case types.LIST:
|
||||
{
|
||||
|
@ -98,9 +101,7 @@ const onAdd = async () => {
|
|||
}
|
||||
const { success, msg } = data
|
||||
if (success) {
|
||||
if (newForm.reload) {
|
||||
browserStore.loadKeyValue(server, db, keyName).then(() => {})
|
||||
}
|
||||
updated = true
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -112,9 +113,7 @@ const onAdd = async () => {
|
|||
{
|
||||
const { success, msg } = await browserStore.addHashField(server, db, keyName, newForm.opType, value)
|
||||
if (success) {
|
||||
if (newForm.reload) {
|
||||
browserStore.loadKeyValue(server, db, keyName).then(() => {})
|
||||
}
|
||||
updated = true
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -126,9 +125,7 @@ const onAdd = async () => {
|
|||
{
|
||||
const { success, msg } = await browserStore.addSetItem(server, db, keyName, value)
|
||||
if (success) {
|
||||
if (newForm.reload) {
|
||||
browserStore.loadKeyValue(server, db, keyName).then(() => {})
|
||||
}
|
||||
updated = true
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -140,9 +137,7 @@ const onAdd = async () => {
|
|||
{
|
||||
const { success, msg } = await browserStore.addZSetItem(server, db, keyName, newForm.opType, value)
|
||||
if (success) {
|
||||
if (newForm.reload) {
|
||||
browserStore.loadKeyValue(server, db, keyName).then(() => {})
|
||||
}
|
||||
updated = true
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -161,9 +156,7 @@ const onAdd = async () => {
|
|||
slice(value, 1),
|
||||
)
|
||||
if (success) {
|
||||
if (newForm.reload) {
|
||||
browserStore.loadKeyValue(server, db, keyName).then(() => {})
|
||||
}
|
||||
updated = true
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
@ -172,6 +165,12 @@ const onAdd = async () => {
|
|||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
if (newForm.reload) {
|
||||
browserStore.reloadKey({ server, db, key: keyName })
|
||||
}
|
||||
}
|
||||
dialogStore.closeAddFieldsDialog()
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
|
|
|
@ -130,7 +130,7 @@ const onAdd = async () => {
|
|||
if (success) {
|
||||
// select current key
|
||||
tabStore.setSelectedKeys(server, nodeKey)
|
||||
browserStore.loadKeyValue(server, db, key).then(() => {})
|
||||
browserStore.loadKeySummary({ server, db, key })
|
||||
} else if (!isEmpty(msg)) {
|
||||
$message.error(msg)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { reactive, watch } from 'vue'
|
|||
import useDialog from 'stores/dialog'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
import useTabStore from 'stores/tab.js'
|
||||
|
||||
const renameForm = reactive({
|
||||
server: '',
|
||||
|
@ -13,6 +14,7 @@ const renameForm = reactive({
|
|||
|
||||
const dialogStore = useDialog()
|
||||
const browserStore = useBrowserStore()
|
||||
const tab = useTabStore()
|
||||
watch(
|
||||
() => dialogStore.renameDialogVisible,
|
||||
(visible) => {
|
||||
|
@ -30,9 +32,10 @@ const i18n = useI18n()
|
|||
const onRename = async () => {
|
||||
try {
|
||||
const { server, db, key, newKey } = renameForm
|
||||
const { success, msg } = await browserStore.renameKey(server, db, key, newKey)
|
||||
const { success, msg, nodeKey } = await browserStore.renameKey(server, db, key, newKey)
|
||||
if (success) {
|
||||
await browserStore.loadKeyValue(server, db, newKey)
|
||||
tab.setSelectedKeys(server, nodeKey)
|
||||
browserStore.loadKeySummary({ server, db, key: newKey })
|
||||
$message.success(i18n.t('dialogue.handle_succ'))
|
||||
} else {
|
||||
$message.error(msg)
|
||||
|
|
|
@ -276,7 +276,11 @@ const handleSelectContextMenu = (key) => {
|
|||
// browserStore.loadKeys(props.server, db, redisKey)
|
||||
// break
|
||||
case 'value_reload':
|
||||
browserStore.loadKeyValue(props.server, db, redisKey)
|
||||
browserStore.reloadKey({
|
||||
server: props.server,
|
||||
db,
|
||||
key: redisKey,
|
||||
})
|
||||
break
|
||||
case 'key_remove':
|
||||
dialogStore.openDeleteKeyDialog(props.server, db, isEmpty(redisKey) ? '*' : redisKey + ':*')
|
||||
|
@ -378,14 +382,18 @@ const onUpdateSelectedKeys = (keys, options) => {
|
|||
const { key, db } = node
|
||||
const redisKey = node.redisKeyCode || node.redisKey
|
||||
if (!includes(selectedKeys.value, key)) {
|
||||
browserStore.loadKeyValue(props.server, db, redisKey)
|
||||
browserStore.loadKeySummary({
|
||||
server: props.server,
|
||||
db,
|
||||
key: redisKey,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// default is load blank key to display server status
|
||||
browserStore.loadKeyValue(props.server, 0)
|
||||
tabStore.openBlank(props.server)
|
||||
} finally {
|
||||
tabStore.setSelectedKeys(props.server, keys)
|
||||
}
|
||||
|
@ -640,7 +648,7 @@ const nodeProps = ({ option }) => {
|
|||
contextMenuParam.x = e.clientX
|
||||
contextMenuParam.y = e.clientY
|
||||
contextMenuParam.show = true
|
||||
tabStore.setSelectedKeys(props.server, option.key)
|
||||
onUpdateSelectedKeys([option.key], [option])
|
||||
})
|
||||
},
|
||||
// onMouseover() {
|
||||
|
|
|
@ -90,6 +90,8 @@
|
|||
"new_key": "Add Key",
|
||||
"load_more": "Load More Keys",
|
||||
"load_all": "Load All Left Keys",
|
||||
"load_more_entries": "Load More",
|
||||
"load_all_entries": "Load All",
|
||||
"more_action": "More Action",
|
||||
"nonexist_tab_content": "Selected key does not exist or no key is selected. Please retry",
|
||||
"empty_server_content": "Select and open a connection from the left",
|
||||
|
|
|
@ -90,6 +90,8 @@
|
|||
"new_key": "添加新键",
|
||||
"load_more": "加载更多键",
|
||||
"load_all": "加载剩余所有键",
|
||||
"load_more_entries": "加载更多",
|
||||
"load_all_entries": "加载全部",
|
||||
"more_action": "更多操作",
|
||||
"nonexist_tab_content": "所选键不存在或未选中任何键,请尝试刷新重试",
|
||||
"empty_server_content": "可以从左边选择并打开连接",
|
||||
|
|
|
@ -24,7 +24,8 @@ import {
|
|||
DeleteKey,
|
||||
FlushDB,
|
||||
GetCmdHistory,
|
||||
GetKeyValue,
|
||||
GetKeyDetail,
|
||||
GetKeySummary,
|
||||
GetSlowLogs,
|
||||
LoadAllKeys,
|
||||
LoadNextKeys,
|
||||
|
@ -58,7 +59,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @property {number} type
|
||||
* @property {number} [db] - database index, type == ConnectionType.RedisDB only
|
||||
* @property {string} [redisKey] - redis key, type == ConnectionType.RedisKey || type == ConnectionType.RedisValue only
|
||||
* @property {string} [redisKeyCode] - redis key char code array, optional for redis key which contains binary data
|
||||
* @property {number[]} [redisKeyCode] - redis key char code array, optional for redis key which contains binary data
|
||||
* @property {number} [keys] - children key count
|
||||
* @property {number} [maxKeys] - max key count for database
|
||||
* @property {boolean} [isLeaf]
|
||||
|
@ -344,20 +345,23 @@ const useBrowserStore = defineStore('browser', {
|
|||
},
|
||||
|
||||
/**
|
||||
* load redis key
|
||||
* load key summary info
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string|number[]} [key] when key is null or blank, update tab to display normal content (blank content or server status)
|
||||
* @param {string} [viewType]
|
||||
* @param {string} [decodeType]
|
||||
* @param {string|number[]} [key] null or blank indicate that update tab to display normal content (blank content or server status)
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async loadKeyValue(server, db, key, viewType, decodeType) {
|
||||
async loadKeySummary({ server, db, key }) {
|
||||
try {
|
||||
const tab = useTabStore()
|
||||
if (!isEmpty(key)) {
|
||||
const { data, success, msg } = await GetKeyValue(server, db, key, viewType, decodeType)
|
||||
const { data, success, msg } = await GetKeySummary({
|
||||
server,
|
||||
db,
|
||||
key,
|
||||
})
|
||||
if (success) {
|
||||
const { type, ttl, value, size, length, viewAs, decode } = data
|
||||
const { type, ttl, size, length } = data
|
||||
const k = decodeRedisKey(key)
|
||||
const binaryKey = k !== key
|
||||
tab.upsertTab({
|
||||
|
@ -368,16 +372,13 @@ const useBrowserStore = defineStore('browser', {
|
|||
ttl,
|
||||
keyCode: binaryKey ? key : undefined,
|
||||
key: k,
|
||||
value,
|
||||
size,
|
||||
length,
|
||||
viewAs,
|
||||
decode,
|
||||
})
|
||||
return
|
||||
} else {
|
||||
if (!isEmpty(msg)) {
|
||||
$message.error('load key fail: ' + msg)
|
||||
$message.error('load key summary fail: ' + msg)
|
||||
}
|
||||
// its danger to delete "non-exists" key, just remove from tree view
|
||||
await this.deleteKey(server, db, key, true)
|
||||
|
@ -397,10 +398,75 @@ const useBrowserStore = defineStore('browser', {
|
|||
size: 0,
|
||||
length: 0,
|
||||
})
|
||||
} catch (e) {
|
||||
$message.error('')
|
||||
} finally {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* reload key
|
||||
* @param server
|
||||
* @param db
|
||||
* @param key
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async reloadKey({ server, db, key }) {
|
||||
const tab = useTabStore()
|
||||
try {
|
||||
tab.updateLoading({ server, db, loading: true })
|
||||
await this.loadKeySummary({ server, db, key })
|
||||
await this.loadKeyDetail({ server, db, key, reset: true })
|
||||
} finally {
|
||||
tab.updateLoading({ server, db, loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* load key content
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string|number[]} key
|
||||
* @param {string} [viewType]
|
||||
* @param {string} [decodeType]
|
||||
* @param {string} [matchPattern]
|
||||
* @param {boolean} [reset]
|
||||
* @param {boolean} [full]
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async loadKeyDetail({ server, db, key, viewType, decodeType, matchPattern, reset, full }) {
|
||||
const tab = useTabStore()
|
||||
try {
|
||||
tab.updateLoading({ server, db, loading: true })
|
||||
const { data, success, msg } = await GetKeyDetail({
|
||||
server,
|
||||
db,
|
||||
key,
|
||||
viewAs: viewType,
|
||||
decodeType,
|
||||
matchPattern,
|
||||
full: full === true,
|
||||
reset,
|
||||
lite: true,
|
||||
})
|
||||
if (success) {
|
||||
const { value, viewAs, decodeType: decode, end } = data
|
||||
tab.updateValue({
|
||||
server,
|
||||
db,
|
||||
key: decodeRedisKey(key),
|
||||
value,
|
||||
viewAs,
|
||||
decode,
|
||||
reset: reset || full === true,
|
||||
end,
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
tab.updateLoading({ server, db, loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* scan keys with prefix
|
||||
* @param {string} connName
|
||||
|
@ -857,7 +923,14 @@ const useBrowserStore = defineStore('browser', {
|
|||
try {
|
||||
const { data, success, msg } = await SetHashValue(connName, db, key, field, newField || '', value || '')
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
const { updated = {}, removed = [], replaced = {} } = data
|
||||
const tab = useTabStore()
|
||||
if (!isEmpty(removed)) {
|
||||
tab.removeValueEntries({ server: connName, db, key, type: 'hash', entries: removed })
|
||||
}
|
||||
if (!isEmpty(updated)) {
|
||||
tab.upsertValueEntries({ server: connName, db, key, type: 'hash', entries: updated })
|
||||
}
|
||||
return { success, updated }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -881,6 +954,8 @@ const useBrowserStore = defineStore('browser', {
|
|||
const { data, success, msg } = await AddHashField(connName, db, key, action, fieldItems)
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({ server: connName, db, key, type: 'hash', entries: updated })
|
||||
return { success, updated }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
|
@ -903,6 +978,10 @@ const useBrowserStore = defineStore('browser', {
|
|||
const { data, success, msg } = await SetHashValue(connName, db, key, field, '', '')
|
||||
if (success) {
|
||||
const { removed = [] } = data
|
||||
if (!isEmpty(removed)) {
|
||||
const tab = useTabStore()
|
||||
tab.removeValueEntries({ server: connName, db, key, type: 'hash', entries: removed })
|
||||
}
|
||||
return { success, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -935,13 +1014,24 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param db
|
||||
* @param key
|
||||
* @param values
|
||||
* @returns {Promise<[msg]: string, success: boolean, [item]: []>}
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [item]: []}>}
|
||||
*/
|
||||
async prependListItem(connName, db, key, values) {
|
||||
try {
|
||||
const { data, success, msg } = await AddListItem(connName, db, key, 0, values)
|
||||
if (success) {
|
||||
const { left = [] } = data
|
||||
if (!isEmpty(left)) {
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({
|
||||
server: connName,
|
||||
db,
|
||||
key,
|
||||
type: 'list',
|
||||
entries: right,
|
||||
prepend: true,
|
||||
})
|
||||
}
|
||||
return { success, item: left }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
|
@ -957,13 +1047,24 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param db
|
||||
* @param key
|
||||
* @param values
|
||||
* @returns {Promise<[msg]: string, success: boolean, [item]: any[]>}
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [item]: any[]}>}
|
||||
*/
|
||||
async appendListItem(connName, db, key, values) {
|
||||
try {
|
||||
const { data, success, msg } = await AddListItem(connName, db, key, 1, values)
|
||||
if (success) {
|
||||
const { right = [] } = data
|
||||
if (!isEmpty(right)) {
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({
|
||||
server: connName,
|
||||
db,
|
||||
key,
|
||||
type: 'list',
|
||||
entries: right,
|
||||
prepend: false,
|
||||
})
|
||||
}
|
||||
return { success, item: right }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
|
@ -987,6 +1088,16 @@ const useBrowserStore = defineStore('browser', {
|
|||
const { data, success, msg } = await SetListItem(connName, db, key, index, value)
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
if (!isEmpty(updated)) {
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({
|
||||
server: connName,
|
||||
db,
|
||||
key,
|
||||
type: 'list',
|
||||
entries: updated,
|
||||
})
|
||||
}
|
||||
return { success, updated }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -1009,6 +1120,16 @@ const useBrowserStore = defineStore('browser', {
|
|||
const { data, success, msg } = await SetListItem(connName, db, key, index, '')
|
||||
if (success) {
|
||||
const { removed = [] } = data
|
||||
if (!isEmpty(removed)) {
|
||||
const tab = useTabStore()
|
||||
tab.removeValueEntries({
|
||||
server: connName,
|
||||
db,
|
||||
key,
|
||||
type: 'list',
|
||||
entries: removed,
|
||||
})
|
||||
}
|
||||
return { success, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -1023,13 +1144,18 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string|number} key
|
||||
* @param {string} value
|
||||
* @param {string|string[]} value
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async addSetItem(connName, db, key, value) {
|
||||
try {
|
||||
const { success, msg } = await SetSetItem(connName, db, key, false, [value])
|
||||
if (!value instanceof Array) {
|
||||
value = [value]
|
||||
}
|
||||
const { data, success, msg } = await SetSetItem(connName, db, key, false, value)
|
||||
if (success) {
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({ server: connName, db, key, type: 'set', entries: value })
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -1052,6 +1178,8 @@ const useBrowserStore = defineStore('browser', {
|
|||
try {
|
||||
const { success, msg } = await UpdateSetItem(connName, db, key, value, newValue)
|
||||
if (success) {
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({ server: connName, db, key, type: 'set', entries: { [value]: newValue } })
|
||||
return { success: true }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -1073,6 +1201,8 @@ const useBrowserStore = defineStore('browser', {
|
|||
try {
|
||||
const { success, msg } = await SetSetItem(connName, db, key, true, [value])
|
||||
if (success) {
|
||||
const tab = useTabStore()
|
||||
tab.removeValueEntries({ server: connName, db, key, type: 'set', entries: [value] })
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -1094,6 +1224,8 @@ const useBrowserStore = defineStore('browser', {
|
|||
async addZSetItem(connName, db, key, action, vs) {
|
||||
try {
|
||||
const { success, msg } = await AddZSetValue(connName, db, key, action, vs)
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({ server: connName, db, key, type: 'zset', entries: vs })
|
||||
if (success) {
|
||||
return { success }
|
||||
} else {
|
||||
|
@ -1119,6 +1251,13 @@ const useBrowserStore = defineStore('browser', {
|
|||
const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, newValue, score)
|
||||
if (success) {
|
||||
const { updated, removed } = data
|
||||
const tab = useTabStore()
|
||||
if (!isEmpty(updated)) {
|
||||
tab.upsertValueEntries({ server: connName, db, key, type: 'zset', entries: updated })
|
||||
}
|
||||
if (!isEmpty(removed)) {
|
||||
tab.removeValueEntries({ server: connName, db, key, type: 'zset', entries: removed })
|
||||
}
|
||||
return { success, updated, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
|
@ -1141,7 +1280,11 @@ const useBrowserStore = defineStore('browser', {
|
|||
const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, '', 0)
|
||||
if (success) {
|
||||
const { removed } = data
|
||||
return { success, removed }
|
||||
if (!isEmpty(removed)) {
|
||||
const tab = useTabStore()
|
||||
tab.removeValueEntries({ server: connName, db, key, type: 'zset', entries: removed })
|
||||
}
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
|
@ -1157,14 +1300,22 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param {string|number[]} key
|
||||
* @param {string} id
|
||||
* @param {string[]} values field1, value1, filed2, value2...
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async addStreamValue(connName, db, key, id, values) {
|
||||
try {
|
||||
const { data = {}, success, msg } = await AddStreamValue(connName, db, key, id, values)
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
return { success, updated }
|
||||
const { updateID } = data
|
||||
const tab = useTabStore()
|
||||
tab.upsertValueEntries({
|
||||
server: connName,
|
||||
db,
|
||||
key,
|
||||
type: 'stream',
|
||||
entries: [{ id: updateID, value: values }],
|
||||
})
|
||||
return { success }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
}
|
||||
|
@ -1179,7 +1330,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param {number} db
|
||||
* @param {string|number[]} key
|
||||
* @param {string[]|string} ids
|
||||
* @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>}
|
||||
* @returns {Promise<{[msg]: {}, success: boolean}>}
|
||||
*/
|
||||
async removeStreamValues(connName, db, key, ids) {
|
||||
if (typeof ids === 'string') {
|
||||
|
@ -1188,8 +1339,9 @@ const useBrowserStore = defineStore('browser', {
|
|||
try {
|
||||
const { data = {}, success, msg } = await RemoveStreamValues(connName, db, key, ids)
|
||||
if (success) {
|
||||
const { removed = [] } = data
|
||||
return { success, removed }
|
||||
const tab = useTabStore()
|
||||
tab.removeValueEntries({ server: connName, db, key, type: 'stream', entries: ids })
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
|
@ -1424,7 +1576,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} newKey
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [nodeKey]: string}>}
|
||||
*/
|
||||
async renameKey(connName, db, key, newKey) {
|
||||
const { success = false, msg } = await RenameKey(connName, db, key, newKey)
|
||||
|
@ -1432,7 +1584,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
// delete old key and add new key struct
|
||||
this._deleteKeyNode(connName, db, key)
|
||||
this._addKeyNodes(connName, db, [newKey])
|
||||
return { success: true }
|
||||
return { success: true, nodeKey: `${connName}/db${db}#${ConnectionType.RedisValue}/${newKey}` }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { find, findIndex, get, isEmpty, set, size } from 'lodash'
|
||||
import { assign, find, findIndex, get, indexOf, isEmpty, pullAt, remove, set, size } from 'lodash'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
const useTabStore = defineStore('tab', {
|
||||
|
@ -11,12 +11,18 @@ const useTabStore = defineStore('tab', {
|
|||
* @property {string} [icon] tab icon
|
||||
* @property {string[]} selectedKeys
|
||||
* @property {string} [type] key type
|
||||
* @property {Object|Array} [value] key value
|
||||
* @property {*} [value] key value
|
||||
* @property {string} [server] server name
|
||||
* @property {int} [db] database index
|
||||
* @property {string} [key] current key name
|
||||
* @property {number[]|null|undefined} [keyCode] current key name as char array
|
||||
* @param {number} [size] memory usage
|
||||
* @param {number} [length] length of content or entries
|
||||
* @property {int} [ttl] ttl of current key
|
||||
* @param {string} [viewAs]
|
||||
* @param {string} [decode]
|
||||
* @param {boolean} [end]
|
||||
* @param {boolean} [loading]
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -82,6 +88,10 @@ const useTabStore = defineStore('tab', {
|
|||
}
|
||||
},
|
||||
|
||||
openBlank(server) {
|
||||
this.upsertTab({ server, db: 0 })
|
||||
},
|
||||
|
||||
/**
|
||||
* update or insert a new tab if not exists with the same name
|
||||
* @param {string} subTab
|
||||
|
@ -94,10 +104,8 @@ const useTabStore = defineStore('tab', {
|
|||
* @param {number} [size]
|
||||
* @param {number} [length]
|
||||
* @param {*} [value]
|
||||
* @param {string} [viewAs]
|
||||
* @param {string} [decode]
|
||||
*/
|
||||
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, length, value, viewAs, decode }) {
|
||||
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, length }) {
|
||||
let tabIndex = findIndex(this.tabList, { name: server })
|
||||
if (tabIndex === -1) {
|
||||
this.tabList.push({
|
||||
|
@ -112,9 +120,7 @@ const useTabStore = defineStore('tab', {
|
|||
keyCode,
|
||||
size,
|
||||
length,
|
||||
value,
|
||||
viewAs,
|
||||
decode,
|
||||
value: undefined,
|
||||
})
|
||||
tabIndex = this.tabList.length - 1
|
||||
} else {
|
||||
|
@ -131,16 +137,239 @@ const useTabStore = defineStore('tab', {
|
|||
tab.keyCode = keyCode
|
||||
tab.size = size
|
||||
tab.length = length
|
||||
tab.value = value
|
||||
tab.viewAs = viewAs
|
||||
tab.decode = decode
|
||||
tab.value = undefined
|
||||
}
|
||||
this._setActivatedIndex(tabIndex, true, subTab)
|
||||
// this.activatedTab = tab.name
|
||||
},
|
||||
|
||||
/**
|
||||
* update ttl by tag
|
||||
* keep update value in tab
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {*} value
|
||||
* @param {string} [viewAs]
|
||||
* @param {string] [decode]
|
||||
* @param {boolean} reset
|
||||
* @param {boolean} [end] keep end status if not set
|
||||
*/
|
||||
updateValue({ server, db, key, value, viewAs, decode, reset, end }) {
|
||||
const tab = find(this.tabList, { name: server, db, key })
|
||||
if (tab == null) {
|
||||
return
|
||||
}
|
||||
|
||||
tab.viewAs = viewAs || tab.viewAs
|
||||
tab.decode = decode || tab.decode
|
||||
if (typeof end === 'boolean') {
|
||||
tab.end = end
|
||||
}
|
||||
if (!reset && typeof value === 'object') {
|
||||
if (value instanceof Array) {
|
||||
tab.value = tab.value || []
|
||||
tab.value.push(...value)
|
||||
} else {
|
||||
tab.value = assign(value, tab.value || {})
|
||||
}
|
||||
} else {
|
||||
tab.value = value
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* update or insert value entries
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} type
|
||||
* @param {string[]|Object.<string, number>|Object.<number, string>} entries
|
||||
* @param {boolean} [prepend] for list only
|
||||
* @param {boolean} [reset]
|
||||
* @param {boolean} [nocheck] ignore conflict checking for hash/set/zset
|
||||
*/
|
||||
upsertValueEntries({ server, db, key, type, entries, prepend, reset, nocheck }) {
|
||||
const tab = find(this.tabList, { name: server, db, key })
|
||||
if (tab == null) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case 'list': // string[] | Object.<number, string>
|
||||
if (entries instanceof Array) {
|
||||
// append or prepend items
|
||||
if (reset === true) {
|
||||
tab.value = entries
|
||||
} else {
|
||||
tab.value = tab.value || []
|
||||
if (prepend === true) {
|
||||
tab.value = [...entries, ...tab.value]
|
||||
} else {
|
||||
tab.value.push(...entries)
|
||||
}
|
||||
tab.length += size(entries)
|
||||
}
|
||||
} else {
|
||||
// replace by index
|
||||
tab.value = tab.value || []
|
||||
for (const idx in entries) {
|
||||
set(tab.value, idx, entries[idx])
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'hash': // Object.<string, string>
|
||||
if (reset === true) {
|
||||
tab.value = {}
|
||||
tab.length = 0
|
||||
} else {
|
||||
tab.value = tab.value || {}
|
||||
}
|
||||
for (const k in entries) {
|
||||
if (nocheck !== true && !tab.value.hasOwnProperty(k)) {
|
||||
tab.length += 1
|
||||
}
|
||||
tab.value[k] = entries[k]
|
||||
}
|
||||
break
|
||||
|
||||
case 'set': // string[] | Object.{string, string}
|
||||
if (reset === true) {
|
||||
tab.value = entries
|
||||
} else {
|
||||
tab.value = tab.value || []
|
||||
if (entries instanceof Array) {
|
||||
// add items
|
||||
for (const elem of entries) {
|
||||
if (nocheck !== true && indexOf(tab.value, elem) === -1) {
|
||||
tab.value.push(elem)
|
||||
tab.length += 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// replace items
|
||||
for (const k in entries) {
|
||||
const idx = indexOf(tab.value, k)
|
||||
if (idx !== -1) {
|
||||
tab.value[idx] = entries[k]
|
||||
} else {
|
||||
tab.value.push(entries[k])
|
||||
tab.length += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'zset': // {value: string, score: number}
|
||||
if (reset === true) {
|
||||
tab.value = Object.entries(entries).map(([value, score]) => ({ value, score }))
|
||||
} else {
|
||||
tab.value = tab.value || []
|
||||
for (const val in entries) {
|
||||
if (nocheck !== true) {
|
||||
const ent = find(tab.value, (e) => e.value === val)
|
||||
if (ent != null) {
|
||||
ent.score = entries[val]
|
||||
} else {
|
||||
tab.value.push({ value: val, score: entries[val] })
|
||||
tab.length += 1
|
||||
}
|
||||
} else {
|
||||
tab.value.push({ value: val, score: entries[val] })
|
||||
tab.length += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'stream': // [{id: string, value: []any}]
|
||||
if (reset === true) {
|
||||
tab.value = entries
|
||||
} else {
|
||||
tab.value = tab.value || []
|
||||
tab.value = [...entries, ...tab.value]
|
||||
}
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* remove value entries
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} type
|
||||
* @param {string[] | number[]} entries
|
||||
*/
|
||||
removeValueEntries({ server, db, key, type, entries }) {
|
||||
const tab = find(this.tabList, { name: server, db, key })
|
||||
if (tab == null) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case 'list': // string[] | number[]
|
||||
tab.value = tab.value || []
|
||||
if (typeof entries[0] === 'number') {
|
||||
// remove by index、
|
||||
entries.sort((a, b) => b - a)
|
||||
const removed = pullAt(tab.value, ...entries)
|
||||
tab.length -= size(removed)
|
||||
} else {
|
||||
// append or prepend items
|
||||
for (const elem of entries) {
|
||||
if (!isEmpty(remove(tab.value, elem))) {
|
||||
tab.length -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'hash': // string[]
|
||||
tab.value = tab.value || {}
|
||||
for (const k of entries) {
|
||||
if (tab.value.hasOwnProperty(k)) {
|
||||
delete tab.value[k]
|
||||
tab.length -= 1
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'set': // []string
|
||||
tab.value = tab.value || []
|
||||
tab.length -= size(remove(tab.value, (v) => entries.indexOf(v) >= 0))
|
||||
break
|
||||
|
||||
case 'zset': // string[]
|
||||
tab.value = tab.value || []
|
||||
tab.length -= size(remove(tab.value, (v) => entries.indexOf(v.value) >= 0))
|
||||
break
|
||||
|
||||
case 'stream': // string[]
|
||||
tab.value = tab.value || []
|
||||
tab.length -= size(remove(tab.value, (v) => entries.indexOf(v.id) >= 0))
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* update loading status of content in tab
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {boolean} loading
|
||||
*/
|
||||
updateLoading({ server, db, loading }) {
|
||||
const tab = find(this.tabList, { name: server, db })
|
||||
if (tab == null) {
|
||||
return
|
||||
}
|
||||
|
||||
tab.loading = loading
|
||||
},
|
||||
|
||||
/**
|
||||
* update ttl in tab
|
||||
* @param {string} server
|
||||
* @param {number} db
|
||||
* @param {string|number[]} key
|
||||
|
|
|
@ -92,7 +92,8 @@ body {
|
|||
.value-wrapper {
|
||||
//border-top: v-bind('themeVars.borderColor') 1px solid;
|
||||
user-select: text;
|
||||
height: 100%;
|
||||
//height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.value-item-part {
|
||||
|
@ -136,3 +137,7 @@ body {
|
|||
.n-modal-mask {
|
||||
--wails-draggable: drag;
|
||||
}
|
||||
|
||||
.n-tabs .n-tabs-nav {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue