Compare commits
10 Commits
e6b28c9edc
...
21c63e2ac2
Author | SHA1 | Date |
---|---|---|
tiny-craft | 21c63e2ac2 | |
tiny-craft | 65e077c0c0 | |
tiny-craft | 5a58a57cd5 | |
Lykin | 73c14204b7 | |
tiny-craft | 7b3c3df230 | |
tiny-craft | fa4929cd63 | |
tiny-craft | 352e7b714d | |
tiny-craft | 9618990de8 | |
tiny-craft | f2ebd7f358 | |
Leonardo Gnutzmann | 3d0310a9c1 |
|
@ -1,6 +1,6 @@
|
|||
## Tiny RDM Contribute Guide
|
||||
|
||||
### Multi-language Contributions {#language}
|
||||
### Multi-language Contributions
|
||||
|
||||
#### Adding New Language
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
|||
```
|
||||
4. Submit review once there are no issues with the translation context in the application. (learn how to submit)
|
||||
|
||||
### Code Submission`(To be completed)` {#pull_request}
|
||||
### Code Submission`(To be completed)`
|
||||
|
||||
#### Pull Request Title
|
||||
The format of PR's title like "<type>: <description>"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## Tiny RDM 代码贡献指南
|
||||
|
||||
### 多国语言贡献 {#language}
|
||||
### 多国语言贡献
|
||||
|
||||
#### 增加新的语言
|
||||
1. 创建文件:在[frontend/src/langs](../frontend/src/langs/)目录下新增语言配置JSON文件,文件名格式为“{语言}-{地区}.json”,如英文为“en-us.json”,简体中文为“zh-cn.json”,建议直接复制[en-us.json](../frontend/src/langs/en-us.json)文件进行改名。
|
||||
|
@ -19,7 +19,7 @@
|
|||
```
|
||||
4. 检查应用中对应翻译语境无问题后,可提交审核([查看如何提交](#pull_request))
|
||||
|
||||
### 代码提交`(待完善)` {#pull_request}
|
||||
### 代码提交`(待完善)`
|
||||
|
||||
#### PR提交规范
|
||||
PR提交格式为“<type>: <description>”
|
||||
|
|
|
@ -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 {
|
||||
|
@ -479,14 +490,355 @@ func (b *browserService) LoadAllKeys(connName string, db int, match, keyType str
|
|||
return
|
||||
}
|
||||
b.setClientCursor(connName, db, 0)
|
||||
maxKeys := b.loadDBSize(ctx, client)
|
||||
|
||||
resp.Success = true
|
||||
resp.Data = map[string]any{
|
||||
"keys": keys,
|
||||
"maxKeys": maxKeys,
|
||||
}
|
||||
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 +1126,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 +1148,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 +1159,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 +1299,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 +1311,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 +1362,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 +1437,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 +1449,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 +1465,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
|
||||
}
|
||||
|
||||
|
@ -1146,23 +1522,25 @@ func (b *browserService) DeleteKey(connName string, db int, k any, async bool) (
|
|||
if strings.HasSuffix(key, "*") {
|
||||
// delete by prefix
|
||||
var mutex sync.Mutex
|
||||
supportUnlink := true
|
||||
del := func(ctx context.Context, cli redis.UniversalClient) error {
|
||||
handleDel := func(ks []string) error {
|
||||
pipe := cli.Pipeline()
|
||||
for _, k2 := range ks {
|
||||
if async {
|
||||
cli.Unlink(ctx, k2)
|
||||
var delErr error
|
||||
if async && supportUnlink {
|
||||
supportUnlink = false
|
||||
if delErr = cli.Unlink(ctx, ks...).Err(); delErr != nil {
|
||||
// not support unlink? try del command
|
||||
delErr = cli.Del(ctx, ks...).Err()
|
||||
}
|
||||
} else {
|
||||
cli.Del(ctx, k2)
|
||||
delErr = cli.Del(ctx, ks...).Err()
|
||||
}
|
||||
}
|
||||
pipe.Exec(ctx)
|
||||
|
||||
mutex.Lock()
|
||||
deletedKeys = append(deletedKeys, ks...)
|
||||
mutex.Unlock()
|
||||
|
||||
return nil
|
||||
return delErr
|
||||
}
|
||||
|
||||
scanSize := int64(Preferences().GetScanSize())
|
||||
|
@ -1170,7 +1548,7 @@ func (b *browserService) DeleteKey(connName string, db int, k any, async bool) (
|
|||
resultKeys := make([]string, 0, 100)
|
||||
for iter.Next(ctx) {
|
||||
resultKeys = append(resultKeys, iter.Val())
|
||||
if len(resultKeys) >= 3 {
|
||||
if len(resultKeys) >= 20 {
|
||||
handleDel(resultKeys)
|
||||
resultKeys = resultKeys[:0:cap(resultKeys)]
|
||||
}
|
||||
|
@ -1198,12 +1576,14 @@ func (b *browserService) DeleteKey(connName string, db int, k any, async bool) (
|
|||
} else {
|
||||
// delete key only
|
||||
if async {
|
||||
if _, err = client.Unlink(ctx, key).Result(); err != nil {
|
||||
if err = client.Unlink(ctx, key).Err(); err != nil {
|
||||
if err = client.Del(ctx, key).Err(); err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err = client.Del(ctx, key).Result(); err != nil {
|
||||
if err = client.Del(ctx, key).Err(); err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
@ -1227,8 +1607,8 @@ func (b *browserService) FlushDB(connName string, db int, async bool) (resp type
|
|||
return
|
||||
}
|
||||
|
||||
flush := func(ctx context.Context, cli redis.UniversalClient) {
|
||||
cli.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
|
||||
flush := func(ctx context.Context, cli redis.UniversalClient, async bool) error {
|
||||
_, e := cli.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
|
||||
pipe.Select(ctx, db)
|
||||
if async {
|
||||
pipe.FlushDBAsync(ctx)
|
||||
|
@ -1237,17 +1617,26 @@ func (b *browserService) FlushDB(connName string, db int, async bool) (resp type
|
|||
}
|
||||
return nil
|
||||
})
|
||||
return e
|
||||
}
|
||||
|
||||
client, ctx := item.client, item.ctx
|
||||
if cluster, ok := client.(*redis.ClusterClient); ok {
|
||||
// cluster mode
|
||||
err = cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
|
||||
flush(ctx, cli)
|
||||
return nil
|
||||
return flush(ctx, cli, async)
|
||||
})
|
||||
// try sync mode if error cause
|
||||
if err != nil && async {
|
||||
err = cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
|
||||
return flush(ctx, cli, false)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
flush(ctx, client)
|
||||
if err = flush(ctx, client, async); err != nil && async {
|
||||
// try sync mode if error cause
|
||||
err = flush(ctx, client, false)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -14,17 +14,17 @@
|
|||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.7",
|
||||
"sass": "^1.69.5",
|
||||
"vue": "^3.3.7",
|
||||
"vue-i18n": "^9.6.2",
|
||||
"vue": "^3.3.8",
|
||||
"vue-i18n": "^9.6.5",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"@vitejs/plugin-vue": "^4.4.1",
|
||||
"naive-ui": "^2.35.0",
|
||||
"prettier": "^3.0.3",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-icons": "^0.17.1",
|
||||
"unplugin-icons": "^0.17.3",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite": "^4.5.0"
|
||||
}
|
||||
|
@ -465,23 +465,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.6.2.tgz",
|
||||
"integrity": "sha512-ci0j2nbEL/pamvqgcCqyIVeQ3LS41F1IRqI5rCBNnpSp0FjNnH8bpha8R3OifkhqatzlP4wGOuN/UqfLYVDv7g==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.6.5.tgz",
|
||||
"integrity": "sha512-LzbGXiZkMWPIHnHI0g6q554S87Cmh2mmCmjytK/3pDQfjI84l+dgGoeQuKj02q7EbULRuUUgYVZVqAwEUawXGg==",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "9.6.2",
|
||||
"@intlify/shared": "9.6.2"
|
||||
"@intlify/message-compiler": "9.6.5",
|
||||
"@intlify/shared": "9.6.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.6.2.tgz",
|
||||
"integrity": "sha512-kgZQL9zeJDeEB5vvD93Y++HvFUELnT48PjnpfCcF3EJaLLVs9he8IzODiNK42Z40lWbFyja0SXJZjsalybQygA==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.6.5.tgz",
|
||||
"integrity": "sha512-WeJ499thIj0p7JaIO1V3JaJbqdqfBykS5R8fElFs5hNeotHtPAMBs4IiA+8/KGFkAbjJusgFefCq6ajP7F7+4Q==",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "9.6.2",
|
||||
"@intlify/shared": "9.6.5",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -489,9 +489,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.6.2.tgz",
|
||||
"integrity": "sha512-9KBcXmJNxElp7QMnU8V0/tScTOitDqyFi4HceEZqJyyDkMi8K5DBPMTIuXIAMmtMlXpe/nj5pke7tRw97VeQRA==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.6.5.tgz",
|
||||
"integrity": "sha512-gD7Ey47Xi4h/t6P+S04ymMSoA3wVRxGqjxuIMglwRO8POki9h164Epu2N8wk/GHXM/hR6ZGcsx2HArCCENjqSQ==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
|
@ -592,9 +592,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
|
||||
"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.4.1.tgz",
|
||||
"integrity": "sha512-HCQG8VDFDM7YDAdcj5QI5DvUi+r6xvo9LgvYdk7LSkUNwdpempdB5horkMSZsbdey9Ywsf5aaU8kEPw9M5kREA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
|
@ -605,36 +605,36 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.7.tgz",
|
||||
"integrity": "sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
|
||||
"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/shared": "3.3.8",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.7.tgz",
|
||||
"integrity": "sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
|
||||
"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-core": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.7.tgz",
|
||||
"integrity": "sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
|
||||
"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@vue/compiler-core": "3.3.7",
|
||||
"@vue/compiler-dom": "3.3.7",
|
||||
"@vue/compiler-ssr": "3.3.7",
|
||||
"@vue/reactivity-transform": "3.3.7",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/compiler-core": "3.3.8",
|
||||
"@vue/compiler-dom": "3.3.8",
|
||||
"@vue/compiler-ssr": "3.3.8",
|
||||
"@vue/reactivity-transform": "3.3.8",
|
||||
"@vue/shared": "3.3.8",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.5",
|
||||
"postcss": "^8.4.31",
|
||||
|
@ -642,12 +642,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.7.tgz",
|
||||
"integrity": "sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
|
||||
"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-dom": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
|
@ -656,41 +656,41 @@
|
|||
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.7.tgz",
|
||||
"integrity": "sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
|
||||
"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity-transform": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.7.tgz",
|
||||
"integrity": "sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
|
||||
"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@vue/compiler-core": "3.3.7",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/compiler-core": "3.3.8",
|
||||
"@vue/shared": "3.3.8",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.7.tgz",
|
||||
"integrity": "sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
|
||||
"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/reactivity": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.7.tgz",
|
||||
"integrity": "sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
|
||||
"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.3.7",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/runtime-core": "3.3.8",
|
||||
"@vue/shared": "3.3.8",
|
||||
"csstype": "^3.1.2"
|
||||
}
|
||||
},
|
||||
|
@ -700,21 +700,21 @@
|
|||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.7.tgz",
|
||||
"integrity": "sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
|
||||
"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-ssr": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.3.7"
|
||||
"vue": "3.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.7.tgz",
|
||||
"integrity": "sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg=="
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
|
||||
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.10.0",
|
||||
|
@ -1744,9 +1744,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/unplugin-icons": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.17.1.tgz",
|
||||
"integrity": "sha512-KsWejBPCHokYCNDQUzGu6R3E3XDYH/YpewgQwrVBXgpl1iR0RdW1NEGNdjlbuapwVnZXVgA5eiDTfNaQCawSdg==",
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.17.3.tgz",
|
||||
"integrity": "sha512-uvopA4I6QYWYpwAyRvG/4NNvR1Cd4GnF/mtOkybgP2YPkHjRX02l/ALm20VUla31Jt9eZHxHyODtNf/Upx5uaw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@antfu/install-pkg": "^0.1.1",
|
||||
|
@ -1906,15 +1906,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.7.tgz",
|
||||
"integrity": "sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
|
||||
"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.3.7",
|
||||
"@vue/compiler-sfc": "3.3.7",
|
||||
"@vue/runtime-dom": "3.3.7",
|
||||
"@vue/server-renderer": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-dom": "3.3.8",
|
||||
"@vue/compiler-sfc": "3.3.8",
|
||||
"@vue/runtime-dom": "3.3.8",
|
||||
"@vue/server-renderer": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
|
@ -1926,12 +1926,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.6.2.tgz",
|
||||
"integrity": "sha512-J43grTQjPR8LCUxvx3mkoM+11xhTnej1Al4lvJCEeKmQqf8eqbuYPQb54HXnEg/UzZyaxLBAwPAUTbrZ8V7hcg==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.6.5.tgz",
|
||||
"integrity": "sha512-dpUEjKHg7pEsaS7ZPPxp1CflaR7bGmsvZJEhnszHPKl9OTNyno5j/DvMtMSo41kpddq4felLA7GK2prjpnXVlw==",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "9.6.2",
|
||||
"@intlify/shared": "9.6.2",
|
||||
"@intlify/core-base": "9.6.5",
|
||||
"@intlify/shared": "9.6.5",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2238,27 +2238,27 @@
|
|||
}
|
||||
},
|
||||
"@intlify/core-base": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.6.2.tgz",
|
||||
"integrity": "sha512-ci0j2nbEL/pamvqgcCqyIVeQ3LS41F1IRqI5rCBNnpSp0FjNnH8bpha8R3OifkhqatzlP4wGOuN/UqfLYVDv7g==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.6.5.tgz",
|
||||
"integrity": "sha512-LzbGXiZkMWPIHnHI0g6q554S87Cmh2mmCmjytK/3pDQfjI84l+dgGoeQuKj02q7EbULRuUUgYVZVqAwEUawXGg==",
|
||||
"requires": {
|
||||
"@intlify/message-compiler": "9.6.2",
|
||||
"@intlify/shared": "9.6.2"
|
||||
"@intlify/message-compiler": "9.6.5",
|
||||
"@intlify/shared": "9.6.5"
|
||||
}
|
||||
},
|
||||
"@intlify/message-compiler": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.6.2.tgz",
|
||||
"integrity": "sha512-kgZQL9zeJDeEB5vvD93Y++HvFUELnT48PjnpfCcF3EJaLLVs9he8IzODiNK42Z40lWbFyja0SXJZjsalybQygA==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.6.5.tgz",
|
||||
"integrity": "sha512-WeJ499thIj0p7JaIO1V3JaJbqdqfBykS5R8fElFs5hNeotHtPAMBs4IiA+8/KGFkAbjJusgFefCq6ajP7F7+4Q==",
|
||||
"requires": {
|
||||
"@intlify/shared": "9.6.2",
|
||||
"@intlify/shared": "9.6.5",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"@intlify/shared": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.6.2.tgz",
|
||||
"integrity": "sha512-9KBcXmJNxElp7QMnU8V0/tScTOitDqyFi4HceEZqJyyDkMi8K5DBPMTIuXIAMmtMlXpe/nj5pke7tRw97VeQRA=="
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.6.5.tgz",
|
||||
"integrity": "sha512-gD7Ey47Xi4h/t6P+S04ymMSoA3wVRxGqjxuIMglwRO8POki9h164Epu2N8wk/GHXM/hR6ZGcsx2HArCCENjqSQ=="
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
|
@ -2336,43 +2336,43 @@
|
|||
}
|
||||
},
|
||||
"@vitejs/plugin-vue": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
|
||||
"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.4.1.tgz",
|
||||
"integrity": "sha512-HCQG8VDFDM7YDAdcj5QI5DvUi+r6xvo9LgvYdk7LSkUNwdpempdB5horkMSZsbdey9Ywsf5aaU8kEPw9M5kREA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.7.tgz",
|
||||
"integrity": "sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
|
||||
"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/shared": "3.3.8",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.7.tgz",
|
||||
"integrity": "sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
|
||||
"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-core": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-sfc": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.7.tgz",
|
||||
"integrity": "sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
|
||||
"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@vue/compiler-core": "3.3.7",
|
||||
"@vue/compiler-dom": "3.3.7",
|
||||
"@vue/compiler-ssr": "3.3.7",
|
||||
"@vue/reactivity-transform": "3.3.7",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/compiler-core": "3.3.8",
|
||||
"@vue/compiler-dom": "3.3.8",
|
||||
"@vue/compiler-ssr": "3.3.8",
|
||||
"@vue/reactivity-transform": "3.3.8",
|
||||
"@vue/shared": "3.3.8",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.5",
|
||||
"postcss": "^8.4.31",
|
||||
|
@ -2380,12 +2380,12 @@
|
|||
}
|
||||
},
|
||||
"@vue/compiler-ssr": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.7.tgz",
|
||||
"integrity": "sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
|
||||
"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-dom": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"@vue/devtools-api": {
|
||||
|
@ -2394,41 +2394,41 @@
|
|||
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
|
||||
},
|
||||
"@vue/reactivity": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.7.tgz",
|
||||
"integrity": "sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
|
||||
"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
|
||||
"requires": {
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"@vue/reactivity-transform": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.7.tgz",
|
||||
"integrity": "sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
|
||||
"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@vue/compiler-core": "3.3.7",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/compiler-core": "3.3.8",
|
||||
"@vue/shared": "3.3.8",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.5"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-core": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.7.tgz",
|
||||
"integrity": "sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
|
||||
"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
|
||||
"requires": {
|
||||
"@vue/reactivity": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/reactivity": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-dom": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.7.tgz",
|
||||
"integrity": "sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
|
||||
"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
|
||||
"requires": {
|
||||
"@vue/runtime-core": "3.3.7",
|
||||
"@vue/shared": "3.3.7",
|
||||
"@vue/runtime-core": "3.3.8",
|
||||
"@vue/shared": "3.3.8",
|
||||
"csstype": "^3.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -2440,18 +2440,18 @@
|
|||
}
|
||||
},
|
||||
"@vue/server-renderer": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.7.tgz",
|
||||
"integrity": "sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
|
||||
"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
|
||||
"requires": {
|
||||
"@vue/compiler-ssr": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-ssr": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.7.tgz",
|
||||
"integrity": "sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg=="
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
|
||||
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.10.0",
|
||||
|
@ -3236,9 +3236,9 @@
|
|||
}
|
||||
},
|
||||
"unplugin-icons": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.17.1.tgz",
|
||||
"integrity": "sha512-KsWejBPCHokYCNDQUzGu6R3E3XDYH/YpewgQwrVBXgpl1iR0RdW1NEGNdjlbuapwVnZXVgA5eiDTfNaQCawSdg==",
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.17.3.tgz",
|
||||
"integrity": "sha512-uvopA4I6QYWYpwAyRvG/4NNvR1Cd4GnF/mtOkybgP2YPkHjRX02l/ALm20VUla31Jt9eZHxHyODtNf/Upx5uaw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@antfu/install-pkg": "^0.1.1",
|
||||
|
@ -3311,24 +3311,24 @@
|
|||
}
|
||||
},
|
||||
"vue": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.7.tgz",
|
||||
"integrity": "sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
|
||||
"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.3.7",
|
||||
"@vue/compiler-sfc": "3.3.7",
|
||||
"@vue/runtime-dom": "3.3.7",
|
||||
"@vue/server-renderer": "3.3.7",
|
||||
"@vue/shared": "3.3.7"
|
||||
"@vue/compiler-dom": "3.3.8",
|
||||
"@vue/compiler-sfc": "3.3.8",
|
||||
"@vue/runtime-dom": "3.3.8",
|
||||
"@vue/server-renderer": "3.3.8",
|
||||
"@vue/shared": "3.3.8"
|
||||
}
|
||||
},
|
||||
"vue-i18n": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.6.2.tgz",
|
||||
"integrity": "sha512-J43grTQjPR8LCUxvx3mkoM+11xhTnej1Al4lvJCEeKmQqf8eqbuYPQb54HXnEg/UzZyaxLBAwPAUTbrZ8V7hcg==",
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.6.5.tgz",
|
||||
"integrity": "sha512-dpUEjKHg7pEsaS7ZPPxp1CflaR7bGmsvZJEhnszHPKl9OTNyno5j/DvMtMSo41kpddq4felLA7GK2prjpnXVlw==",
|
||||
"requires": {
|
||||
"@intlify/core-base": "9.6.2",
|
||||
"@intlify/shared": "9.6.2",
|
||||
"@intlify/core-base": "9.6.5",
|
||||
"@intlify/shared": "9.6.5",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.7",
|
||||
"sass": "^1.69.5",
|
||||
"vue": "^3.3.7",
|
||||
"vue-i18n": "^9.6.2",
|
||||
"vue": "^3.3.8",
|
||||
"vue-i18n": "^9.6.5",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"@vitejs/plugin-vue": "^4.4.1",
|
||||
"naive-ui": "^2.35.0",
|
||||
"prettier": "^3.0.3",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-icons": "^0.17.1",
|
||||
"unplugin-icons": "^0.17.3",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite": "^4.5.0"
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
24a0be173ef3e3939a15eb9c7fff5e1a
|
||||
3a2923aff6e49c16c4ca2e7fc8811eeb
|
|
@ -26,7 +26,7 @@ const props = defineProps({
|
|||
|
||||
const data = reactive({
|
||||
navMenuWidth: 60,
|
||||
toolbarHeight: 45,
|
||||
toolbarHeight: 38,
|
||||
})
|
||||
|
||||
const tabStore = useTabStore()
|
||||
|
@ -135,7 +135,7 @@ onMounted(async () => {
|
|||
paddingLeft: `${logoPaddingLeft}px`,
|
||||
}">
|
||||
<n-space :size="3" :wrap="false" :wrap-item="false" align="center">
|
||||
<n-avatar :size="35" :src="iconUrl" color="#0000" style="min-width: 35px" />
|
||||
<n-avatar :size="32" :src="iconUrl" color="#0000" style="min-width: 32px" />
|
||||
<div style="min-width: 68px; font-weight: 800">Tiny RDM</div>
|
||||
<transition name="fade">
|
||||
<n-text v-if="tabStore.nav === 'browser'" class="ellipsis" strong style="font-size: 13px">
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
@ -262,20 +261,28 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset: () => {
|
||||
clearFilter()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 +301,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 +324,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)
|
||||
}
|
||||
|
@ -194,20 +194,28 @@ const clearFilter = () => {
|
|||
const onUpdateFilter = (filters, sourceColumn) => {
|
||||
valueColumn.filterOptionValue = filters[sourceColumn.key]
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset: () => {
|
||||
clearFilter()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 +226,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 +249,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)
|
||||
}
|
||||
|
@ -189,20 +195,28 @@ const clearFilter = () => {
|
|||
const onUpdateFilter = (filters, sourceColumn) => {
|
||||
valueColumn.filterOptionValue = filters[sourceColumn.key]
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset: () => {
|
||||
clearFilter()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 +227,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 +250,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)
|
||||
}
|
||||
|
@ -174,20 +184,28 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset: () => {
|
||||
clearFilter()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 +224,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 +247,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 +266,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, ref, 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,108 @@ 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)
|
||||
})
|
||||
|
||||
const contentRef = ref(null)
|
||||
watch(
|
||||
() => data.value?.keyPath,
|
||||
() => {
|
||||
// onReload()
|
||||
if (contentRef.value?.reset != null) {
|
||||
contentRef.value?.reset()
|
||||
}
|
||||
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]"
|
||||
ref="contentRef"
|
||||
: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)
|
||||
}
|
||||
|
@ -287,20 +299,28 @@ const onUpdateFilter = (filters, sourceColumn) => {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset: () => {
|
||||
clearFilter()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 +344,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 +367,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)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { reactive, watch } from 'vue'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import useDialog from 'stores/dialog'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { isEmpty, size } from 'lodash'
|
||||
|
@ -29,18 +29,22 @@ watch(
|
|||
deleteForm.loadingAffected = false
|
||||
deleteForm.affectedKeys = []
|
||||
deleteForm.async = true
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const loading = ref(false)
|
||||
const scanAffectedKey = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
deleteForm.loadingAffected = true
|
||||
const { keys = [] } = await browserStore.scanKeys(deleteForm.server, deleteForm.db, deleteForm.key)
|
||||
deleteForm.affectedKeys = keys || []
|
||||
deleteForm.showAffected = true
|
||||
} finally {
|
||||
deleteForm.loadingAffected = false
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +56,7 @@ const resetAffected = () => {
|
|||
const i18n = useI18n()
|
||||
const onConfirmDelete = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const { server, db, key, async } = deleteForm
|
||||
const success = await browserStore.deleteKeyPrefix(server, db, key, async)
|
||||
if (success) {
|
||||
|
@ -59,6 +64,9 @@ const onConfirmDelete = async () => {
|
|||
}
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
return
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
dialogStore.closeDeleteKeyDialog()
|
||||
}
|
||||
|
@ -78,6 +86,7 @@ const onClose = () => {
|
|||
:title="$t('interface.batch_delete_key')"
|
||||
preset="dialog"
|
||||
transform-origin="center">
|
||||
<n-spin :show="loading">
|
||||
<n-form :model="deleteForm" :show-require-mark="false" label-placement="top">
|
||||
<n-form-item :label="$t('dialogue.key.server')">
|
||||
<n-input :value="deleteForm.server" readonly />
|
||||
|
@ -89,9 +98,14 @@ const onClose = () => {
|
|||
<n-input v-model:value="deleteForm.key" placeholder="" @input="resetAffected" />
|
||||
</n-form-item>
|
||||
<n-form-item :label="$t('dialogue.key.async_delete')" required>
|
||||
<n-checkbox v-model:checked="deleteForm.async">{{ $t('dialogue.key.async_delete_title') }}</n-checkbox>
|
||||
<n-checkbox v-model:checked="deleteForm.async">
|
||||
{{ $t('dialogue.key.async_delete_title') }}
|
||||
</n-checkbox>
|
||||
</n-form-item>
|
||||
<n-card v-if="deleteForm.showAffected" :title="$t('dialogue.key.affected_key')" size="small">
|
||||
<n-card
|
||||
v-if="deleteForm.showAffected"
|
||||
:title="$t('dialogue.key.affected_key') + `(${size(deleteForm.affectedKeys)})`"
|
||||
size="small">
|
||||
<n-skeleton v-if="deleteForm.loadingAffected" :repeat="10" text />
|
||||
<n-log
|
||||
v-else
|
||||
|
@ -101,17 +115,24 @@ const onClose = () => {
|
|||
style="user-select: text; cursor: text" />
|
||||
</n-card>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
|
||||
<template #action>
|
||||
<div class="flex-item n-dialog__action">
|
||||
<n-button :focusable="false" @click="onClose">{{ $t('common.cancel') }}</n-button>
|
||||
<n-button v-if="!deleteForm.showAffected" :focusable="false" type="primary" @click="scanAffectedKey">
|
||||
<n-button :disabled="loading" :focusable="false" @click="onClose">{{ $t('common.cancel') }}</n-button>
|
||||
<n-button
|
||||
v-if="!deleteForm.showAffected"
|
||||
:focusable="false"
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
@click="scanAffectedKey">
|
||||
{{ $t('dialogue.key.show_affected_key') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
v-else
|
||||
:disabled="isEmpty(deleteForm.affectedKeys)"
|
||||
:focusable="false"
|
||||
:loading="loading"
|
||||
type="error"
|
||||
@click="onConfirmDelete">
|
||||
{{ $t('dialogue.key.confirm_delete_key', { num: size(deleteForm.affectedKeys) }) }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { reactive, watch } from 'vue'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import useDialog from 'stores/dialog'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import useBrowserStore from 'stores/browser.js'
|
||||
|
@ -23,13 +23,16 @@ watch(
|
|||
flushForm.db = db
|
||||
flushForm.async = true
|
||||
flushForm.confirm = false
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const loading = ref(false)
|
||||
const i18n = useI18n()
|
||||
const onConfirmFlush = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const { server, db, async } = flushForm
|
||||
const success = await browserStore.flushDatabase(server, db, async)
|
||||
if (success) {
|
||||
|
@ -37,6 +40,9 @@ const onConfirmFlush = async () => {
|
|||
}
|
||||
} catch (e) {
|
||||
$message.error(e.message)
|
||||
return
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
dialogStore.closeFlushDBDialog()
|
||||
}
|
||||
|
@ -56,6 +62,7 @@ const onClose = () => {
|
|||
:title="$t('interface.flush_db')"
|
||||
preset="dialog"
|
||||
transform-origin="center">
|
||||
<n-spin :show="loading">
|
||||
<n-form :model="flushForm" :show-require-mark="false" label-placement="top">
|
||||
<n-form-item :label="$t('dialogue.key.server')">
|
||||
<n-input :value="flushForm.server" readonly />
|
||||
|
@ -64,7 +71,9 @@ const onClose = () => {
|
|||
<n-input :value="flushForm.db.toString()" readonly />
|
||||
</n-form-item>
|
||||
<n-form-item :label="$t('dialogue.key.async_delete')" required>
|
||||
<n-checkbox v-model:checked="flushForm.async">{{ $t('dialogue.key.async_delete_title') }}</n-checkbox>
|
||||
<n-checkbox v-model:checked="flushForm.async">
|
||||
{{ $t('dialogue.key.async_delete_title') }}
|
||||
</n-checkbox>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$t('common.warning')" required>
|
||||
<n-checkbox v-model:checked="flushForm.confirm">
|
||||
|
@ -72,10 +81,16 @@ const onClose = () => {
|
|||
</n-checkbox>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
|
||||
<template #action>
|
||||
<n-button :focusable="false" @click="onClose">{{ $t('common.cancel') }}</n-button>
|
||||
<n-button :disabled="!!!flushForm.confirm" :focusable="false" type="primary" @click="onConfirmFlush">
|
||||
<n-button :disabled="loading" :focusable="false" @click="onClose">{{ $t('common.cancel') }}</n-button>
|
||||
<n-button
|
||||
:disabled="!!!flushForm.confirm"
|
||||
:focusable="false"
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
@click="onConfirmFlush">
|
||||
{{ $t('dialogue.key.confirm_flush_db') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -272,11 +272,20 @@ const handleSelectContextMenu = (key) => {
|
|||
const { match: pattern, type } = browserStore.getKeyFilter(props.server, db)
|
||||
dialogStore.openKeyFilterDialog(props.server, db, pattern, type)
|
||||
break
|
||||
// case 'key_reload':
|
||||
// browserStore.loadKeys(props.server, db, redisKey)
|
||||
// break
|
||||
case 'key_reload':
|
||||
if (node != null && !!!node.loading) {
|
||||
node.loading = true
|
||||
browserStore.reloadLayer(props.server, db, redisKey).finally(() => {
|
||||
delete node.loading
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'value_reload':
|
||||
browserStore.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 +387,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)
|
||||
}
|
||||
|
@ -511,7 +524,7 @@ const renderIconMenu = (items) => {
|
|||
)
|
||||
}
|
||||
|
||||
const getDatabaseMenu = (opened, loading, end) => {
|
||||
const calcDBMenu = (opened, loading, end) => {
|
||||
const btns = []
|
||||
if (opened) {
|
||||
btns.push(
|
||||
|
@ -573,14 +586,16 @@ const getDatabaseMenu = (opened, loading, end) => {
|
|||
return btns
|
||||
}
|
||||
|
||||
const getLayerMenu = () => {
|
||||
const calcLayerMenu = (loading, end) => {
|
||||
return [
|
||||
// disable reload by layer, due to conflict with partial loading keys
|
||||
// h(IconButton, {
|
||||
// tTooltip: 'interface.reload',
|
||||
// icon: Refresh,
|
||||
// onClick: () => handleSelectContextMenu('key_reload'),
|
||||
// }),
|
||||
// reload layer enable only full loaded
|
||||
h(IconButton, {
|
||||
tTooltip: end === true ? 'interface.reload' : 'interface.reload',
|
||||
icon: Refresh,
|
||||
loading: loading === true,
|
||||
disabled: end !== true,
|
||||
onClick: () => handleSelectContextMenu('key_reload'),
|
||||
}),
|
||||
h(IconButton, {
|
||||
tTooltip: 'interface.new_key',
|
||||
icon: Add,
|
||||
|
@ -594,7 +609,7 @@ const getLayerMenu = () => {
|
|||
]
|
||||
}
|
||||
|
||||
const getValueMenu = () => {
|
||||
const calcValueMenu = () => {
|
||||
return [
|
||||
h(IconButton, {
|
||||
tTooltip: 'interface.remove_key',
|
||||
|
@ -609,11 +624,12 @@ const renderSuffix = ({ option }) => {
|
|||
if ((option.type === ConnectionType.RedisDB && option.opened) || includes(selectedKeys.value, option.key)) {
|
||||
switch (option.type) {
|
||||
case ConnectionType.RedisDB:
|
||||
return renderIconMenu(getDatabaseMenu(option.opened, option.loading, option.fullLoaded))
|
||||
return renderIconMenu(calcDBMenu(option.opened, option.loading, option.fullLoaded))
|
||||
case ConnectionType.RedisKey:
|
||||
return renderIconMenu(getLayerMenu())
|
||||
const fullLoaded = browserStore.isFullLoaded(props.server, option.db)
|
||||
return renderIconMenu(calcLayerMenu(option.loading, fullLoaded))
|
||||
case ConnectionType.RedisValue:
|
||||
return renderIconMenu(getValueMenu())
|
||||
return renderIconMenu(calcValueMenu())
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
@ -640,7 +656,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",
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import en from './en-us'
|
||||
import pt from './pt-br'
|
||||
import zh from './zh-cn'
|
||||
|
||||
export const lang = {
|
||||
en,
|
||||
pt,
|
||||
zh,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
{
|
||||
"name": "Português",
|
||||
"common": {
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar",
|
||||
"success": "Sucesso",
|
||||
"warning": "Aviso",
|
||||
"error": "Erro",
|
||||
"save": "Salvar",
|
||||
"none": "Nenhum",
|
||||
"second": "Segundo(s)",
|
||||
"unit_day": "D",
|
||||
"unit_hour": "H",
|
||||
"unit_minute": "M",
|
||||
"all": "Tudo",
|
||||
"key": "Chave",
|
||||
"value": "Valor",
|
||||
"field": "Campo"
|
||||
},
|
||||
"preferences": {
|
||||
"name": "Preferências",
|
||||
"restore_defaults": "Restaurar Padrões",
|
||||
"general": {
|
||||
"name": "Geral",
|
||||
"theme": "Tema",
|
||||
"theme_light": "Claro",
|
||||
"theme_dark": "Escuro",
|
||||
"theme_auto": "Automático",
|
||||
"language": "Idioma",
|
||||
"system_lang": "Usar Idioma do Sistema",
|
||||
"default": "Padrão",
|
||||
"font": "Fonte",
|
||||
"font_size": "Tamanho da Fonte",
|
||||
"scan_size": "Tamanho Padrão para Comando SCAN",
|
||||
"proxy": "Proxy",
|
||||
"use_system_proxy": "Usar proxy do sistema",
|
||||
"use_system_proxy_http": "Usar proxy do sistema apenas para solicitações HTTP(S)",
|
||||
"update": "Atualizar",
|
||||
"auto_check_update": "Verificar atualizações automaticamente"
|
||||
},
|
||||
"editor": {
|
||||
"name": "Editor"
|
||||
}
|
||||
},
|
||||
"interface": {
|
||||
"new_conn": "Adicionar Nova Conexão",
|
||||
"new_group": "Adicionar Novo Grupo",
|
||||
"disconnect_all": "Desconectar tudo",
|
||||
"status": "Status",
|
||||
"filter": "Filtro",
|
||||
"sort_conn": "Ordenar Conexões",
|
||||
"new_conn_title": "Nova Conexão",
|
||||
"open_db": "Abrir Banco de Dados",
|
||||
"close_db": "Fechar Banco de Dados",
|
||||
"filter_key": "Filtrar Chave",
|
||||
"disconnect": "Desconectar",
|
||||
"dup_conn": "Duplicar Conexão",
|
||||
"remove_conn": "Excluir Conexão",
|
||||
"edit_conn": "Editar Configuração da Conexão",
|
||||
"edit_conn_group": "Editar Grupo de Conexão",
|
||||
"rename_conn_group": "Renomear Grupo de Conexão",
|
||||
"remove_conn_group": "Excluir Grupo de Conexão",
|
||||
"ttl": "TTL",
|
||||
"forever": "Para Sempre",
|
||||
"rename_key": "Renomear Chave",
|
||||
"delete_key": "Excluir Chave",
|
||||
"batch_delete_key": "Excluir Lotes de Chaves",
|
||||
"flush_db": "Limpar Banco de Dados",
|
||||
"copy_value": "Copiar Valor",
|
||||
"edit_value": "Editar Valor",
|
||||
"save_update": "Salvar Atualização",
|
||||
"score_filter_tip": "Lista de operadores suportados abaixo:\n= igual\n!= diferente\n> maior que\n>= maior ou igual a\n< menor que\n<= menor ou igual a\nPor exemplo, se você deseja filtrar resultados maiores que 3, insira: >3",
|
||||
"add_row": "Adicionar Linha",
|
||||
"edit_row": "Editar Linha",
|
||||
"delete_row": "Excluir Linha",
|
||||
"search": "Buscar",
|
||||
"filter_field": "Filtrar Campo",
|
||||
"filter_value": "Filtrar Valor",
|
||||
"length": "Tamanho",
|
||||
"entries": "Entradas",
|
||||
"memory_usage": "Uso de Memória",
|
||||
"view_as": "Visualizar Como",
|
||||
"decode_with": "Decodificar / Descompressão",
|
||||
"reload": "Recarregar",
|
||||
"open_connection": "Abrir Conexão",
|
||||
"copy_path": "Copiar Caminho",
|
||||
"copy_key": "Copiar Chave",
|
||||
"binary_key": "Nome da Chave Binária",
|
||||
"remove_key": "Remover Chave",
|
||||
"new_key": "Adicionar Chave",
|
||||
"load_more": "Carregar Mais Chaves",
|
||||
"load_all": "Carregar Todas as Chaves Restantes",
|
||||
"more_action": "Mais Ação",
|
||||
"nonexist_tab_content": "A chave selecionada não existe ou nenhuma chave está selecionada. Por favor, tente novamente",
|
||||
"empty_server_content": "Selecione e abra uma conexão à esquerda",
|
||||
"empty_server_list": "Nenhum servidor Redis",
|
||||
"action": "Ação",
|
||||
"type": "Tipo",
|
||||
"score": "Pontuação",
|
||||
"cli_welcome": "Bem-vindo ao Console Redis Tiny RDM",
|
||||
"sub_tab": {
|
||||
"status": "Status",
|
||||
"key_detail": "Detalhes da Chave",
|
||||
"cli": "Console",
|
||||
"slow_log": "Log Lento",
|
||||
"cmd_monitor": "Monitorar Comandos",
|
||||
"pub_message": "Pub/Sub"
|
||||
}
|
||||
},
|
||||
"ribbon": {
|
||||
"server": "Servidor",
|
||||
"browser": "Navegador de Dados",
|
||||
"log": "Log"
|
||||
},
|
||||
"dialogue": {
|
||||
"close_confirm": "Confirmar o fechamento desta guia e conexão ({name})",
|
||||
"edit_close_confirm": "Por favor, feche as conexões relevantes antes de editar. Deseja continuar?",
|
||||
"opening_connection": "Abrindo Conexão...",
|
||||
"interrupt_connection": "Cancelar",
|
||||
"remove_tip": "{type} \"{name}\" será excluído",
|
||||
"remove_group_tip": "O grupo \"{name}\" e todas as conexões nele serão excluídos",
|
||||
"delete_key_succ": "\"{key}\" foi excluída",
|
||||
"save_value_succ": "Valor Salvo!",
|
||||
"copy_succ": "Valor Copiado!",
|
||||
"rename_binary_key_fail": "Renomear nome de chave binária não é suportado",
|
||||
"handle_succ": "Sucesso!",
|
||||
"reload_succ": "Recarregado!",
|
||||
"field_required": "Este item não deve ficar em branco",
|
||||
"spec_field_required": "\"{key}\" não deve ficar em branco",
|
||||
"illegal_characters": "Inclui caracteres ilegais",
|
||||
"connection": {
|
||||
"new_title": "Nova Conexão",
|
||||
"edit_title": "Editar Conexão",
|
||||
"general": "Geral",
|
||||
"no_group": "Sem Grupo",
|
||||
"group": "Grupo",
|
||||
"conn_name": "Nome",
|
||||
"addr": "Endereço",
|
||||
"usr": "Nome de Usuário",
|
||||
"pwd": "Senha",
|
||||
"name_tip": "Nome da Conexão",
|
||||
"addr_tip": "Host do servidor Redis",
|
||||
"usr_tip": "(Opcional) Nome de usuário para autenticação",
|
||||
"pwd_tip": "(Opcional) Senha de autenticação (Redis > 6.0)",
|
||||
"test": "Testar Conexão",
|
||||
"test_succ": "Conexão bem-sucedida ao servidor Redis",
|
||||
"test_fail": "Falha na Conexão",
|
||||
"advn": {
|
||||
"title": "Avançado",
|
||||
"filter": "Padrão de Filtro de Chave Padrão",
|
||||
"filter_tip": "Padrão que define as chaves carregadas do servidor Redis",
|
||||
"separator": "Separador de Chave",
|
||||
"separator_tip": "Separador para item do caminho da chave",
|
||||
"conn_timeout": "Tempo Limite de Conexão",
|
||||
"exec_timeout": "Tempo Limite de Execução",
|
||||
"dbfilter_type": "Filtro de Banco de Dados",
|
||||
"dbfilter_all": "Mostrar Todos",
|
||||
"dbfilter_show": "Mostrar Selecionados",
|
||||
"dbfilter_hide": "Ocultar Selecionados",
|
||||
"dbfilter_show_title": "Selecione os Bancos de Dados para Mostrar",
|
||||
"dbfilter_hide_title": "Selecione os Bancos de Dados para Ocultar",
|
||||
"dbfilter_input": "Índice do Banco de Dados de Entrada",
|
||||
"dbfilter_input_tip": "Pressione Enter para confirmar",
|
||||
"key_view": "Visualização Padrão de Chave",
|
||||
"key_view_tree": "Visualização em Árvore",
|
||||
"key_view_list": "Visualização em Lista",
|
||||
"load_size": "Tamanho das Chaves por Carga",
|
||||
"mark_color": "Cor de Marcação"
|
||||
},
|
||||
"ssl": {
|
||||
"title": "SSL/TLS",
|
||||
"enable": "Habilitar SSL/TLS",
|
||||
"cert_file": "Chave Pública",
|
||||
"key_file": "Chave Privada",
|
||||
"ca_file": "Autoridade",
|
||||
"cert_file_tip": "Arquivo de Chave Pública no formato PEM (Cert)",
|
||||
"key_file_tip": "Arquivo de Chave Privada no formato PEM (Chave)",
|
||||
"ca_file_tip": "Arquivo de Autoridade de Certificação no formato PEM (CA)"
|
||||
},
|
||||
"ssh": {
|
||||
"title": "Túnel SSH",
|
||||
"enable": "Habilitar Túnel SSH",
|
||||
"login_type": "Tipo de Login",
|
||||
"pkfile": "Arquivo de Chave Privada",
|
||||
"passphrase": "Frase de Senha",
|
||||
"addr_tip": "Host do Servidor SSH",
|
||||
"usr_tip": "Nome de Usuário SSH",
|
||||
"pwd_tip": "Senha SSH",
|
||||
"pkfile_tip": "Caminho do Arquivo de Chave Privada SSH",
|
||||
"passphrase_tip": "(Opcional) Frase de Senha para Chave Privada"
|
||||
},
|
||||
"sentinel": {
|
||||
"title": "Sentinela",
|
||||
"enable": "Atuar como Nó Sentinela",
|
||||
"master": "Nome do Grupo Master",
|
||||
"auto_discover": "Auto Descoberta",
|
||||
"password": "Senha para Nó Master",
|
||||
"username": "Nome de Usuário para Nó Master",
|
||||
"pwd_tip": "(Opcional) Nome de usuário para autenticação no nó master",
|
||||
"usr_tip": "(Opcional) Senha de autenticação no nó master (Redis > 6.0)"
|
||||
},
|
||||
"cluster": {
|
||||
"title": "Cluster",
|
||||
"enable": "Atuar como Nó Cluster",
|
||||
"readonly": "Habilitar comandos somente leitura em nós escravos"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"name": "Nome do Grupo",
|
||||
"rename": "Renomear Grupo",
|
||||
"new": "Novo Grupo"
|
||||
},
|
||||
"key": {
|
||||
"new": "Nova Chave",
|
||||
"new_name": "Novo Nome da Chave",
|
||||
"persist_key": "Persistir Chave",
|
||||
"server": "Conexão",
|
||||
"db_index": "Índice do Banco de Dados",
|
||||
"key_expression": "Expressão da Chave",
|
||||
"affected_key": "Chaves Afetadas",
|
||||
"show_affected_key": "Mostrar Chaves Afetadas",
|
||||
"confirm_delete_key": "Confirmar Exclusão de {num} Chave(s)",
|
||||
"async_delete": "Executar de Forma Assíncrona",
|
||||
"async_delete_title": "Não esperar pelo resultado da operação",
|
||||
"confirm_flush": "Eu sei o que estou fazendo!",
|
||||
"confirm_flush_db": "Confirmar Limpar Banco de Dados"
|
||||
},
|
||||
"field": {
|
||||
"new": "Adicionar Novo Campo",
|
||||
"new_item": "Adicionar Novo Item",
|
||||
"overwrite_field": "Sobrescrever Campo Existente",
|
||||
"ignore_field": "Ignorar Campo Existente",
|
||||
"insert_type": "Inserir",
|
||||
"append_item": "Anexar",
|
||||
"prepend_item": "Inserir no Início",
|
||||
"enter_key": "Digite a Chave",
|
||||
"enter_value": "Digite o Valor",
|
||||
"enter_field": "Digite o Nome do Campo",
|
||||
"enter_elem": "Digite o Elemento",
|
||||
"enter_member": "Digite o Membro",
|
||||
"enter_score": "Digite a Pontuação",
|
||||
"element": "Elemento",
|
||||
"reload_when_succ": "Recarregar imediatamente após o sucesso"
|
||||
},
|
||||
"filter": {
|
||||
"set_key_filter": "Definir Filtro de Chave",
|
||||
"filter_pattern": "Padrão",
|
||||
"filter_pattern_tip": "prefixo_*: Corresponde a nomes de chaves que começam com \"prefixo_\".\n*_sufixo: Corresponde a nomes de chaves que terminam com \"_sufixo\".\n*padrão*: Corresponde a nomes de chaves que contêm \"padrão\".\nprefixo_??: Corresponde a nomes de chaves que começam com \"prefixo_\" seguido por dois caracteres.\n*abc*: Corresponde a nomes de chaves que contêm \"abc\" em qualquer posição."
|
||||
},
|
||||
"ttl": {
|
||||
"title": "Definir TTL da Chave"
|
||||
},
|
||||
"upgrade": {
|
||||
"title": "Nova Versão Disponível",
|
||||
"new_version_tip": "Uma nova versão ({ver}) está disponível. Baixar agora?",
|
||||
"no_update": "Você está atualizado",
|
||||
"download_now": "Baixar",
|
||||
"later": "Mais Tarde",
|
||||
"skip": "Pular Esta Atualização"
|
||||
},
|
||||
"about": {
|
||||
"source": "Código Fonte",
|
||||
"website": "Site Oficial"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"minimise": "Minimizar",
|
||||
"maximise": "Maximizar",
|
||||
"restore": "Restaurar",
|
||||
"close": "Fechar",
|
||||
"preferences": "Preferências",
|
||||
"help": "Ajuda",
|
||||
"check_update": "Verificar Atualizações...",
|
||||
"about": "Sobre"
|
||||
},
|
||||
"log": {
|
||||
"title": "Registro de Inicialização",
|
||||
"filter_server": "Filtrar Servidor",
|
||||
"filter_keyword": "Filtrar Palavra-chave",
|
||||
"clean_log": "Limpar Registro de Inicialização",
|
||||
"confirm_clean_log": "Confirmar limpar registro de inicialização",
|
||||
"exec_time": "Tempo de Execução",
|
||||
"server": "Servidor",
|
||||
"cmd": "Comando",
|
||||
"cost_time": "Custo",
|
||||
"refresh": "Atualizar"
|
||||
},
|
||||
"status": {
|
||||
"uptime": "Tempo de Atividade",
|
||||
"connected_clients": "Clientes Conectados",
|
||||
"total_keys": "Chaves Totais",
|
||||
"memory_used": "Memória Usada",
|
||||
"all_info": "Informações",
|
||||
"refresh": "Atualizar",
|
||||
"auto_refresh": "Atualização Automática"
|
||||
},
|
||||
"slog": {
|
||||
"title": "Registro de Operações Lentas",
|
||||
"limit": "Limite",
|
||||
"filter": "Filtrar",
|
||||
"exec_time": "Tempo",
|
||||
"client": "Cliente",
|
||||
"cmd": "Comando",
|
||||
"cost_time": "Custo",
|
||||
"refresh": "Atualizar Agora",
|
||||
"auto_refresh": "Atualização Automática"
|
||||
}
|
||||
}
|
|
@ -90,6 +90,8 @@
|
|||
"new_key": "添加新键",
|
||||
"load_more": "加载更多键",
|
||||
"load_all": "加载剩余所有键",
|
||||
"load_more_entries": "加载更多",
|
||||
"load_all_entries": "加载全部",
|
||||
"more_action": "更多操作",
|
||||
"nonexist_tab_content": "所选键不存在或未选中任何键,请尝试刷新重试",
|
||||
"empty_server_content": "可以从左边选择并打开连接",
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
get,
|
||||
isEmpty,
|
||||
join,
|
||||
last,
|
||||
remove,
|
||||
set,
|
||||
size,
|
||||
|
@ -24,7 +25,8 @@ import {
|
|||
DeleteKey,
|
||||
FlushDB,
|
||||
GetCmdHistory,
|
||||
GetKeyValue,
|
||||
GetKeyDetail,
|
||||
GetKeySummary,
|
||||
GetSlowLogs,
|
||||
LoadAllKeys,
|
||||
LoadNextKeys,
|
||||
|
@ -58,7 +60,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]
|
||||
|
@ -137,7 +139,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
* get database by server name and index
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @return {{}|null}
|
||||
* @return {DatabaseItem|null}
|
||||
*/
|
||||
getDatabase(connName, db) {
|
||||
const dbs = this.databases[connName]
|
||||
|
@ -150,6 +152,20 @@ const useBrowserStore = defineStore('browser', {
|
|||
return null
|
||||
},
|
||||
|
||||
/**
|
||||
* get full loaded status of database
|
||||
* @param connName
|
||||
* @param db
|
||||
* @return {boolean}
|
||||
*/
|
||||
isFullLoaded(connName, db) {
|
||||
const selDB = this.getDatabase(connName, db)
|
||||
if (selDB != null) {
|
||||
return selDB.fullLoaded === true
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
/**
|
||||
* switch key view
|
||||
* @param {string} connName
|
||||
|
@ -344,20 +360,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 +387,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,18 +413,83 @@ 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
|
||||
* @param {number} db
|
||||
* @param {string} match
|
||||
* @param {string} matchType
|
||||
* @param {string} [matchType]
|
||||
* @param {boolean} [full]
|
||||
* @returns {Promise<{keys: string[], end: boolean}>}
|
||||
* @returns {Promise<{keys: string[], maxKeys: number, end: boolean}>}
|
||||
*/
|
||||
async scanKeys(connName, db, match, matchType, full) {
|
||||
let resp
|
||||
|
@ -421,8 +502,8 @@ const useBrowserStore = defineStore('browser', {
|
|||
if (!success) {
|
||||
throw new Error(msg)
|
||||
}
|
||||
const { keys = [], end } = data
|
||||
return { keys, end, success }
|
||||
const { keys = [], maxKeys, end } = data
|
||||
return { keys, end, maxKeys, success }
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -432,7 +513,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
* @param {string|null} prefix
|
||||
* @param {string|null} matchType
|
||||
* @param {boolean} [all]
|
||||
* @return {Promise<{keys: Array<string|number[]>, end: boolean}>}
|
||||
* @return {Promise<{keys: Array<string|number[]>, maxKeys: number, end: boolean}>}
|
||||
* @private
|
||||
*/
|
||||
async _loadKeys(connName, db, prefix, matchType, all) {
|
||||
|
@ -456,7 +537,8 @@ const useBrowserStore = defineStore('browser', {
|
|||
*/
|
||||
async loadMoreKeys(connName, db) {
|
||||
const { match, type: keyType } = this.getKeyFilter(connName, db)
|
||||
const { keys, end } = await this._loadKeys(connName, db, match, keyType, false)
|
||||
const { keys, maxKeys, end } = await this._loadKeys(connName, db, match, keyType, false)
|
||||
this._setDBMaxKeys(connName, db, maxKeys)
|
||||
// remove current keys below prefix
|
||||
this._addKeyNodes(connName, db, keys)
|
||||
this._tidyNode(connName, db, '')
|
||||
|
@ -471,11 +553,44 @@ const useBrowserStore = defineStore('browser', {
|
|||
*/
|
||||
async loadAllKeys(connName, db) {
|
||||
const { match, type: keyType } = this.getKeyFilter(connName, db)
|
||||
const { keys } = await this._loadKeys(connName, db, match, keyType, true)
|
||||
const { keys, maxKeys } = await this._loadKeys(connName, db, match, keyType, true)
|
||||
this._setDBMaxKeys(connName, db, maxKeys)
|
||||
this._addKeyNodes(connName, db, keys)
|
||||
this._tidyNode(connName, db, '')
|
||||
},
|
||||
|
||||
/**
|
||||
* reload keys under layer
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} prefix
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async reloadLayer(connName, db, prefix) {
|
||||
if (isEmpty(prefix)) {
|
||||
return
|
||||
}
|
||||
let match = prefix
|
||||
const separator = this._getSeparator(connName)
|
||||
if (!endsWith(match, separator)) {
|
||||
match += separator + '*'
|
||||
} else {
|
||||
match += '*'
|
||||
}
|
||||
// FIXME: ignore original match pattern due to redis not support combination matching
|
||||
const { match: originMatch, type: keyType } = this.getKeyFilter(connName, db)
|
||||
const { keys, maxKeys, success } = await this._loadKeys(connName, db, match, keyType, true)
|
||||
if (!success) {
|
||||
return
|
||||
}
|
||||
|
||||
this._setDBMaxKeys(connName, db, maxKeys)
|
||||
// remove current keys below prefix
|
||||
this._deleteKeyNode(connName, db, prefix, true)
|
||||
this._addKeyNodes(connName, db, keys)
|
||||
this._tidyNode(connName, db, prefix)
|
||||
},
|
||||
|
||||
/**
|
||||
* get custom separator of connection
|
||||
* @param server
|
||||
|
@ -749,13 +864,16 @@ const useBrowserStore = defineStore('browser', {
|
|||
},
|
||||
|
||||
/**
|
||||
* update max key by value
|
||||
* update max key by increase/decrease value
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {number} updateValue
|
||||
* @param {number} [updateValue]
|
||||
* @private
|
||||
*/
|
||||
_updateDBMaxKeys(connName, db, updateValue) {
|
||||
if (updateValue === undefined) {
|
||||
return
|
||||
}
|
||||
const database = this.getDatabase(connName, db)
|
||||
if (database != null) {
|
||||
const maxKeys = get(database, 'maxKeys', 0)
|
||||
|
@ -764,15 +882,16 @@ const useBrowserStore = defineStore('browser', {
|
|||
},
|
||||
|
||||
/**
|
||||
* set db max keys to 0
|
||||
* @param connName
|
||||
* @param db
|
||||
* set db max keys value
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {number} maxKeys
|
||||
* @private
|
||||
*/
|
||||
_emptyDBMaxKeys(connName, db) {
|
||||
_setDBMaxKeys(connName, db, maxKeys) {
|
||||
const database = this.getDatabase(connName, db)
|
||||
if (database != null) {
|
||||
set(database, 'maxKeys', 0)
|
||||
set(database, 'maxKeys', maxKeys)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -857,7 +976,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 +1007,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 +1031,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 +1067,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 +1100,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 +1141,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 +1173,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 +1197,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 +1231,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 +1254,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 +1277,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 +1304,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 +1333,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 +1353,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 +1383,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 +1392,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 }
|
||||
}
|
||||
|
@ -1215,6 +1420,34 @@ const useBrowserStore = defineStore('browser', {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} newKey
|
||||
* @private
|
||||
*/
|
||||
_renameKeyNode(connName, db, key, newKey) {
|
||||
const nodeMap = this._getNodeMap(connName, db)
|
||||
const nodeKey = `${ConnectionType.RedisValue}/${key}`
|
||||
const newNodeKey = `${ConnectionType.RedisValue}/${newKey}`
|
||||
const node = nodeMap.get(nodeKey)
|
||||
if (node != null) {
|
||||
// replace node map item
|
||||
const separator = this._getSeparator(connName)
|
||||
node.label = last(split(newKey, separator))
|
||||
node.key = `${connName}/db${db}#${newNodeKey}`
|
||||
node.redisKey = newKey
|
||||
nodeMap[newNodeKey] = node
|
||||
nodeMap.delete(nodeKey)
|
||||
// replace key set item
|
||||
const keySet = this._getKeySet(connName, db)
|
||||
keySet.delete(key)
|
||||
keySet.add(newKey)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} connName
|
||||
|
@ -1407,7 +1640,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
if (success === true) {
|
||||
// update tree view data
|
||||
this._deleteKeyNode(connName, db)
|
||||
this._emptyDBMaxKeys(connName, db)
|
||||
this._setDBMaxKeys(connName, db, 0)
|
||||
// set tab content empty
|
||||
const tab = useTabStore()
|
||||
tab.emptyTab(connName)
|
||||
|
@ -1424,15 +1657,14 @@ 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)
|
||||
if (success) {
|
||||
// delete old key and add new key struct
|
||||
this._deleteKeyNode(connName, db, key)
|
||||
this._addKeyNodes(connName, db, [newKey])
|
||||
return { success: true }
|
||||
this._renameKeyNode(connName, db, key, newKey)
|
||||
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;
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -5,9 +5,9 @@ go 1.21
|
|||
require (
|
||||
github.com/adrg/sysfont v0.1.2
|
||||
github.com/andybalholm/brotli v1.0.6
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/klauspost/compress v1.17.2
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
github.com/redis/go-redis/v9 v9.3.0
|
||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
|
||||
github.com/wailsapp/wails/v2 v2.6.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
|
|
8
go.sum
8
go.sum
|
@ -23,8 +23,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
|
@ -62,8 +62,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg=
|
||||
github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
|
||||
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
|
|
Loading…
Reference in New Issue