Compare commits
9 Commits
aafa0c5432
...
bfb5407030
Author | SHA1 | Date |
---|---|---|
Lykin | bfb5407030 | |
Lykin | 4ffbdbd39a | |
Lykin | 30d6da85a1 | |
Lykin | 9aaf32e2bf | |
Lykin | 7b18ed0b26 | |
Lykin | e7fd13d608 | |
Lykin | 6a048037b0 | |
Lykin | eca640fc87 | |
Lykin | db4e2385fc |
|
@ -251,6 +251,8 @@ func (b *browserService) getRedisClient(connName string, db int) (item connectio
|
||||||
err = fmt.Errorf("create conenction error: %s", err.Error())
|
err = fmt.Errorf("create conenction error: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = client.Do(b.ctx, "CLIENT", "SETNAME", url.QueryEscape(selConn.Name)).Err()
|
||||||
// add hook to each node in cluster mode
|
// add hook to each node in cluster mode
|
||||||
var cluster *redis.ClusterClient
|
var cluster *redis.ClusterClient
|
||||||
if cluster, ok = client.(*redis.ClusterClient); ok {
|
if cluster, ok = client.(*redis.ClusterClient); ok {
|
||||||
|
@ -595,7 +597,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
|
|
||||||
// define get entry cursor function
|
// define get entry cursor function
|
||||||
getEntryCursor := func() (uint64, string) {
|
getEntryCursor := func() (uint64, string, bool) {
|
||||||
if entry, ok := entryCors[param.DB]; !ok || entry.Key != key || entry.Pattern != matchPattern {
|
if entry, ok := entryCors[param.DB]; !ok || entry.Key != key || entry.Pattern != matchPattern {
|
||||||
// not the same key or match pattern, reset cursor
|
// not the same key or match pattern, reset cursor
|
||||||
entry = entryCursor{
|
entry = entryCursor{
|
||||||
|
@ -605,9 +607,9 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
Cursor: 0,
|
Cursor: 0,
|
||||||
}
|
}
|
||||||
entryCors[param.DB] = entry
|
entryCors[param.DB] = entry
|
||||||
return 0, ""
|
return 0, "", true
|
||||||
} else {
|
} else {
|
||||||
return entry.Cursor, entry.XLast
|
return entry.Cursor, entry.XLast, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// define set entry cursor function
|
// define set entry cursor function
|
||||||
|
@ -639,19 +641,21 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
//data.Value, data.Decode, data.Format = strutil.ConvertTo(str, param.Decode, param.Format)
|
//data.Value, data.Decode, data.Format = strutil.ConvertTo(str, param.Decode, param.Format)
|
||||||
|
|
||||||
case "list":
|
case "list":
|
||||||
loadListHandle := func() ([]types.ListEntryItem, bool, error) {
|
loadListHandle := func() ([]types.ListEntryItem, bool, bool, error) {
|
||||||
var loadVal []string
|
var loadVal []string
|
||||||
var cursor uint64
|
var cursor uint64
|
||||||
|
var reset bool
|
||||||
var subErr error
|
var subErr error
|
||||||
if param.Full {
|
doFilter := matchPattern != "*"
|
||||||
|
if param.Full || doFilter {
|
||||||
// load all
|
// load all
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
loadVal, subErr = client.LRange(ctx, key, 0, -1).Result()
|
loadVal, subErr = client.LRange(ctx, key, 0, -1).Result()
|
||||||
} else {
|
} else {
|
||||||
if param.Reset {
|
if param.Reset {
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
} else {
|
} else {
|
||||||
cursor, _ = getEntryCursor()
|
cursor, _, reset = getEntryCursor()
|
||||||
}
|
}
|
||||||
scanSize := int64(Preferences().GetScanSize())
|
scanSize := int64(Preferences().GetScanSize())
|
||||||
loadVal, subErr = client.LRange(ctx, key, int64(cursor), int64(cursor)+scanSize-1).Result()
|
loadVal, subErr = client.LRange(ctx, key, int64(cursor), int64(cursor)+scanSize-1).Result()
|
||||||
|
@ -662,42 +666,54 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
setEntryCursor(cursor)
|
setEntryCursor(cursor)
|
||||||
|
|
||||||
items := make([]types.ListEntryItem, len(loadVal))
|
items := make([]types.ListEntryItem, 0, len(loadVal))
|
||||||
for i, val := range loadVal {
|
for _, val := range loadVal {
|
||||||
items[i].Value = val
|
if doFilter && !strings.Contains(val, param.MatchPattern) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items = append(items, types.ListEntryItem{
|
||||||
|
Value: val,
|
||||||
|
})
|
||||||
if doConvert {
|
if doConvert {
|
||||||
if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val {
|
if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val {
|
||||||
items[i].DisplayValue = dv
|
items[len(items)-1].DisplayValue = dv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if subErr != nil {
|
if subErr != nil {
|
||||||
return items, false, subErr
|
return items, reset, false, subErr
|
||||||
}
|
}
|
||||||
return items, cursor == 0, nil
|
return items, reset, cursor == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Value, data.End, err = loadListHandle()
|
data.Value, data.Reset, data.End, err = loadListHandle()
|
||||||
data.Decode, data.Format = param.Decode, param.Format
|
data.Match, data.Decode, data.Format = param.MatchPattern, param.Decode, param.Format
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case "hash":
|
case "hash":
|
||||||
loadHashHandle := func() ([]types.HashEntryItem, bool, error) {
|
if !strings.HasPrefix(matchPattern, "*") {
|
||||||
|
matchPattern = "*" + matchPattern
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(matchPattern, "*") {
|
||||||
|
matchPattern = matchPattern + "*"
|
||||||
|
}
|
||||||
|
loadHashHandle := func() ([]types.HashEntryItem, bool, bool, error) {
|
||||||
var items []types.HashEntryItem
|
var items []types.HashEntryItem
|
||||||
var loadedVal []string
|
var loadedVal []string
|
||||||
var cursor uint64
|
var cursor uint64
|
||||||
|
var reset bool
|
||||||
var subErr error
|
var subErr error
|
||||||
scanSize := int64(Preferences().GetScanSize())
|
scanSize := int64(Preferences().GetScanSize())
|
||||||
if param.Full {
|
if param.Full || matchPattern != "*" {
|
||||||
// load all
|
// load all
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
for {
|
for {
|
||||||
loadedVal, cursor, subErr = client.HScan(ctx, key, cursor, "*", scanSize).Result()
|
loadedVal, cursor, subErr = client.HScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||||
if subErr != nil {
|
if subErr != nil {
|
||||||
return nil, false, subErr
|
return nil, reset, false, subErr
|
||||||
}
|
}
|
||||||
for i := 0; i < len(loadedVal); i += 2 {
|
for i := 0; i < len(loadedVal); i += 2 {
|
||||||
items = append(items, types.HashEntryItem{
|
items = append(items, types.HashEntryItem{
|
||||||
|
@ -716,13 +732,13 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if param.Reset {
|
if param.Reset {
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
} else {
|
} else {
|
||||||
cursor, _ = getEntryCursor()
|
cursor, _, reset = getEntryCursor()
|
||||||
}
|
}
|
||||||
loadedVal, cursor, subErr = client.HScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
loadedVal, cursor, subErr = client.HScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||||
if subErr != nil {
|
if subErr != nil {
|
||||||
return nil, false, subErr
|
return nil, reset, false, subErr
|
||||||
}
|
}
|
||||||
loadedLen := len(loadedVal)
|
loadedLen := len(loadedVal)
|
||||||
items = make([]types.HashEntryItem, loadedLen/2)
|
items = make([]types.HashEntryItem, loadedLen/2)
|
||||||
|
@ -737,30 +753,37 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setEntryCursor(cursor)
|
setEntryCursor(cursor)
|
||||||
return items, cursor == 0, nil
|
return items, reset, cursor == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Value, data.End, err = loadHashHandle()
|
data.Value, data.Reset, data.End, err = loadHashHandle()
|
||||||
data.Decode, data.Format = param.Decode, param.Format
|
data.Match, data.Decode, data.Format = param.MatchPattern, param.Decode, param.Format
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case "set":
|
case "set":
|
||||||
loadSetHandle := func() ([]types.SetEntryItem, bool, error) {
|
if !strings.HasPrefix(matchPattern, "*") {
|
||||||
|
matchPattern = "*" + matchPattern
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(matchPattern, "*") {
|
||||||
|
matchPattern = matchPattern + "*"
|
||||||
|
}
|
||||||
|
loadSetHandle := func() ([]types.SetEntryItem, bool, bool, error) {
|
||||||
var items []types.SetEntryItem
|
var items []types.SetEntryItem
|
||||||
var cursor uint64
|
var cursor uint64
|
||||||
|
var reset bool
|
||||||
var subErr error
|
var subErr error
|
||||||
var loadedKey []string
|
var loadedKey []string
|
||||||
scanSize := int64(Preferences().GetScanSize())
|
scanSize := int64(Preferences().GetScanSize())
|
||||||
if param.Full {
|
if param.Full || matchPattern != "*" {
|
||||||
// load all
|
// load all
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
for {
|
for {
|
||||||
loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||||
if subErr != nil {
|
if subErr != nil {
|
||||||
return items, false, subErr
|
return items, reset, false, subErr
|
||||||
}
|
}
|
||||||
for _, val := range loadedKey {
|
for _, val := range loadedKey {
|
||||||
items = append(items, types.SetEntryItem{
|
items = append(items, types.SetEntryItem{
|
||||||
|
@ -778,11 +801,11 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if param.Reset {
|
if param.Reset {
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
} else {
|
} else {
|
||||||
cursor, _ = getEntryCursor()
|
cursor, _, reset = getEntryCursor()
|
||||||
}
|
}
|
||||||
loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||||
items = make([]types.SetEntryItem, len(loadedKey))
|
items = make([]types.SetEntryItem, len(loadedKey))
|
||||||
for i, val := range loadedKey {
|
for i, val := range loadedKey {
|
||||||
items[i].Value = val
|
items[i].Value = val
|
||||||
|
@ -794,29 +817,36 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setEntryCursor(cursor)
|
setEntryCursor(cursor)
|
||||||
return items, cursor == 0, nil
|
return items, reset, cursor == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Value, data.End, err = loadSetHandle()
|
data.Value, data.Reset, data.End, err = loadSetHandle()
|
||||||
data.Decode, data.Format = param.Decode, param.Format
|
data.Match, data.Decode, data.Format = param.MatchPattern, param.Decode, param.Format
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case "zset":
|
case "zset":
|
||||||
loadZSetHandle := func() ([]types.ZSetEntryItem, bool, error) {
|
if !strings.HasPrefix(matchPattern, "*") {
|
||||||
|
matchPattern = "*" + matchPattern
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(matchPattern, "*") {
|
||||||
|
matchPattern = matchPattern + "*"
|
||||||
|
}
|
||||||
|
loadZSetHandle := func() ([]types.ZSetEntryItem, bool, bool, error) {
|
||||||
var items []types.ZSetEntryItem
|
var items []types.ZSetEntryItem
|
||||||
|
var reset bool
|
||||||
var cursor uint64
|
var cursor uint64
|
||||||
scanSize := int64(Preferences().GetScanSize())
|
scanSize := int64(Preferences().GetScanSize())
|
||||||
var loadedVal []string
|
var loadedVal []string
|
||||||
if param.Full {
|
if param.Full || matchPattern != "*" {
|
||||||
// load all
|
// load all
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
for {
|
for {
|
||||||
loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
loadedVal, cursor, err = client.ZScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return items, false, err
|
return items, reset, false, err
|
||||||
}
|
}
|
||||||
var score float64
|
var score float64
|
||||||
for i := 0; i < len(loadedVal); i += 2 {
|
for i := 0; i < len(loadedVal); i += 2 {
|
||||||
|
@ -838,11 +868,11 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if param.Reset {
|
if param.Reset {
|
||||||
cursor = 0
|
cursor, reset = 0, true
|
||||||
} else {
|
} else {
|
||||||
cursor, _ = getEntryCursor()
|
cursor, _, reset = getEntryCursor()
|
||||||
}
|
}
|
||||||
loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result()
|
loadedVal, cursor, err = client.ZScan(ctx, key, cursor, matchPattern, scanSize).Result()
|
||||||
loadedLen := len(loadedVal)
|
loadedLen := len(loadedVal)
|
||||||
items = make([]types.ZSetEntryItem, loadedLen/2)
|
items = make([]types.ZSetEntryItem, loadedLen/2)
|
||||||
var score float64
|
var score float64
|
||||||
|
@ -859,30 +889,32 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setEntryCursor(cursor)
|
setEntryCursor(cursor)
|
||||||
return items, cursor == 0, nil
|
return items, reset, cursor == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Value, data.End, err = loadZSetHandle()
|
data.Value, data.Reset, data.End, err = loadZSetHandle()
|
||||||
data.Decode, data.Format = param.Decode, param.Format
|
data.Match, data.Decode, data.Format = param.MatchPattern, param.Decode, param.Format
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case "stream":
|
case "stream":
|
||||||
loadStreamHandle := func() ([]types.StreamEntryItem, bool, error) {
|
loadStreamHandle := func() ([]types.StreamEntryItem, bool, bool, error) {
|
||||||
var msgs []redis.XMessage
|
var msgs []redis.XMessage
|
||||||
var last string
|
var last string
|
||||||
if param.Full {
|
var reset bool
|
||||||
|
doFilter := matchPattern != "*"
|
||||||
|
if param.Full || doFilter {
|
||||||
// load all
|
// load all
|
||||||
last = ""
|
last, reset = "", true
|
||||||
msgs, err = client.XRevRange(ctx, key, "+", "-").Result()
|
msgs, err = client.XRevRange(ctx, key, "+", "-").Result()
|
||||||
} else {
|
} else {
|
||||||
scanSize := int64(Preferences().GetScanSize())
|
scanSize := int64(Preferences().GetScanSize())
|
||||||
if param.Reset {
|
if param.Reset {
|
||||||
last = ""
|
last = ""
|
||||||
} else {
|
} else {
|
||||||
_, last = getEntryCursor()
|
_, last, reset = getEntryCursor()
|
||||||
}
|
}
|
||||||
if len(last) <= 0 {
|
if len(last) <= 0 {
|
||||||
last = "+"
|
last = "+"
|
||||||
|
@ -902,24 +934,30 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setEntryXLast(last)
|
setEntryXLast(last)
|
||||||
items := make([]types.StreamEntryItem, len(msgs))
|
items := make([]types.StreamEntryItem, 0, len(msgs))
|
||||||
for i, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
items[i].ID = msg.ID
|
it := types.StreamEntryItem{
|
||||||
items[i].Value = msg.Values
|
ID: msg.ID,
|
||||||
if vb, merr := json.Marshal(msg.Values); merr != nil {
|
Value: msg.Values,
|
||||||
items[i].DisplayValue = "{}"
|
|
||||||
} else {
|
|
||||||
items[i].DisplayValue, _, _ = strutil.ConvertTo(string(vb), types.DECODE_NONE, types.FORMAT_JSON)
|
|
||||||
}
|
}
|
||||||
|
if vb, merr := json.Marshal(msg.Values); merr != nil {
|
||||||
|
it.DisplayValue = "{}"
|
||||||
|
} else {
|
||||||
|
it.DisplayValue, _, _ = strutil.ConvertTo(string(vb), types.DECODE_NONE, types.FORMAT_JSON)
|
||||||
|
}
|
||||||
|
if doFilter && !strings.Contains(it.DisplayValue, param.MatchPattern) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items = append(items, it)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return items, false, err
|
return items, reset, false, err
|
||||||
}
|
}
|
||||||
return items, last == "", nil
|
return items, reset, last == "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Value, data.End, err = loadStreamHandle()
|
data.Value, data.Reset, data.End, err = loadStreamHandle()
|
||||||
data.Decode, data.Format = param.Decode, param.Format
|
data.Match, data.Decode, data.Format = param.MatchPattern, param.Decode, param.Format
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
|
|
@ -21,8 +21,8 @@ type cliService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type cliOutput struct {
|
type cliOutput struct {
|
||||||
Content string `json:"content"` // output content
|
Content []string `json:"content"` // output content
|
||||||
Prompt string `json:"prompt,omitempty"` // new line prompt, empty if not ready to input
|
Prompt string `json:"prompt,omitempty"` // new line prompt, empty if not ready to input
|
||||||
}
|
}
|
||||||
|
|
||||||
var cli *cliService
|
var cli *cliService
|
||||||
|
@ -54,7 +54,7 @@ func (c *cliService) runCommand(server, data string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.echo(server, strutil.AnyToString(result), true)
|
c.echo(server, strutil.AnyToString(result, "", 0), true)
|
||||||
} else {
|
} else {
|
||||||
c.echoError(server, err.Error())
|
c.echoError(server, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ func (c *cliService) runCommand(server, data string) {
|
||||||
|
|
||||||
func (c *cliService) echo(server, data string, newLineReady bool) {
|
func (c *cliService) echo(server, data string, newLineReady bool) {
|
||||||
output := cliOutput{
|
output := cliOutput{
|
||||||
Content: data,
|
Content: strings.Split(data, "\n"),
|
||||||
}
|
}
|
||||||
if newLineReady {
|
if newLineReady {
|
||||||
output.Prompt = fmt.Sprintf("%s:db%d> ", server, c.selectedDB[server])
|
output.Prompt = fmt.Sprintf("%s:db%d> ", server, c.selectedDB[server])
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -120,7 +119,6 @@ func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.O
|
||||||
}
|
}
|
||||||
|
|
||||||
option := &redis.Options{
|
option := &redis.Options{
|
||||||
ClientName: url.QueryEscape(config.Name),
|
|
||||||
Addr: fmt.Sprintf("%s:%d", config.Addr, config.Port),
|
Addr: fmt.Sprintf("%s:%d", config.Addr, config.Port),
|
||||||
Username: config.Username,
|
Username: config.Username,
|
||||||
Password: config.Password,
|
Password: config.Password,
|
||||||
|
@ -174,7 +172,6 @@ func (c *connectionService) createRedisClient(config types.ConnectionConfig) (re
|
||||||
//RouteByLatency: false,
|
//RouteByLatency: false,
|
||||||
//RouteRandomly: false,
|
//RouteRandomly: false,
|
||||||
//ClusterSlots: nil,
|
//ClusterSlots: nil,
|
||||||
ClientName: url.QueryEscape(option.ClientName),
|
|
||||||
Dialer: option.Dialer,
|
Dialer: option.Dialer,
|
||||||
OnConnect: option.OnConnect,
|
OnConnect: option.OnConnect,
|
||||||
Protocol: option.Protocol,
|
Protocol: option.Protocol,
|
||||||
|
|
|
@ -35,6 +35,8 @@ type KeyDetail struct {
|
||||||
Length int64 `json:"length,omitempty"`
|
Length int64 `json:"length,omitempty"`
|
||||||
Format string `json:"format,omitempty"`
|
Format string `json:"format,omitempty"`
|
||||||
Decode string `json:"decode,omitempty"`
|
Decode string `json:"decode,omitempty"`
|
||||||
|
Match string `json:"match,omitempty"`
|
||||||
|
Reset bool `json:"reset"`
|
||||||
End bool `json:"end"`
|
End bool `json:"end"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
sliceutil "tinyrdm/backend/utils/slice"
|
sliceutil "tinyrdm/backend/utils/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AnyToString(value interface{}) (s string) {
|
func AnyToString(value interface{}, prefix string, layer int) (s string) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,11 @@ func AnyToString(value interface{}) (s string) {
|
||||||
it := value.(uint64)
|
it := value.(uint64)
|
||||||
s = strconv.FormatUint(it, 10)
|
s = strconv.FormatUint(it, 10)
|
||||||
case string:
|
case string:
|
||||||
s = value.(string)
|
if layer > 0 {
|
||||||
|
s = "\"" + value.(string) + "\""
|
||||||
|
} else {
|
||||||
|
s = value.(string)
|
||||||
|
}
|
||||||
case bool:
|
case bool:
|
||||||
val, _ := value.(bool)
|
val, _ := value.(bool)
|
||||||
if val {
|
if val {
|
||||||
|
@ -58,24 +62,44 @@ func AnyToString(value interface{}) (s string) {
|
||||||
s = "False"
|
s = "False"
|
||||||
}
|
}
|
||||||
case []byte:
|
case []byte:
|
||||||
s = string(value.([]byte))
|
s = prefix + string(value.([]byte))
|
||||||
case []string:
|
case []string:
|
||||||
ss := value.([]string)
|
ss := value.([]string)
|
||||||
anyStr := sliceutil.Map(ss, func(i int) string {
|
anyStr := sliceutil.Map(ss, func(i int) string {
|
||||||
str := AnyToString(ss[i])
|
str := AnyToString(ss[i], prefix, layer+1)
|
||||||
return strconv.Itoa(i+1) + ") \"" + str + "\""
|
return prefix + strconv.Itoa(i+1) + ") " + str
|
||||||
})
|
})
|
||||||
s = sliceutil.JoinString(anyStr, "\r\n")
|
s = prefix + sliceutil.JoinString(anyStr, "\r\n")
|
||||||
case []any:
|
case []any:
|
||||||
as := value.([]any)
|
as := value.([]any)
|
||||||
anyItems := sliceutil.Map(as, func(i int) string {
|
anyItems := sliceutil.Map(as, func(i int) string {
|
||||||
str := AnyToString(as[i])
|
str := AnyToString(as[i], prefix, layer+1)
|
||||||
return strconv.Itoa(i+1) + ") \"" + str + "\""
|
return prefix + strconv.Itoa(i+1) + ") " + str
|
||||||
})
|
})
|
||||||
s = sliceutil.JoinString(anyItems, "\r\n")
|
s = sliceutil.JoinString(anyItems, "\r\n")
|
||||||
|
case map[any]any:
|
||||||
|
am := value.(map[any]any)
|
||||||
|
var items []string
|
||||||
|
index := 0
|
||||||
|
for k, v := range am {
|
||||||
|
kk := prefix + strconv.Itoa(index+1) + ") " + AnyToString(k, prefix, layer+1)
|
||||||
|
vv := prefix + strconv.Itoa(index+2) + ") " + AnyToString(v, "\t", layer+1)
|
||||||
|
if layer > 0 {
|
||||||
|
indent := layer
|
||||||
|
if index == 0 {
|
||||||
|
indent -= 1
|
||||||
|
}
|
||||||
|
for i := 0; i < indent; i++ {
|
||||||
|
vv = " " + vv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += 2
|
||||||
|
items = append(items, kk, vv)
|
||||||
|
}
|
||||||
|
s = sliceutil.JoinString(items, "\r\n")
|
||||||
default:
|
default:
|
||||||
b, _ := json.Marshal(value)
|
b, _ := json.Marshal(value)
|
||||||
s = string(b)
|
s = prefix + string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -57,6 +57,7 @@ const tabContent = computed(() => {
|
||||||
length: tab.length || 0,
|
length: tab.length || 0,
|
||||||
decode: tab.decode || decodeTypes.NONE,
|
decode: tab.decode || decodeTypes.NONE,
|
||||||
format: tab.format || formatTypes.RAW,
|
format: tab.format || formatTypes.RAW,
|
||||||
|
matchPattern: tab.matchPattern || '',
|
||||||
end: tab.end === true,
|
end: tab.end === true,
|
||||||
loading: tab.loading === true,
|
loading: tab.loading === true,
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,7 +203,7 @@ const moveInputCursor = (step) => {
|
||||||
} else {
|
} else {
|
||||||
// update cursor position only
|
// update cursor position only
|
||||||
const currentLine = getCurrentInput()
|
const currentLine = getCurrentInput()
|
||||||
inputCursor = Math.min(Math.max(0, inputCursor), currentLine.length)
|
inputCursor = Math.max(0, currentLine.length)
|
||||||
updateCursor = true
|
updateCursor = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,16 +351,18 @@ const replaceTermInput = (content = '') => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* process receive output content
|
* process receive output content
|
||||||
* @param {{content, prompt}} data
|
* @param {{content: string[], prompt: string}} data
|
||||||
*/
|
*/
|
||||||
const receiveTermOutput = (data) => {
|
const receiveTermOutput = (data) => {
|
||||||
if (termInst == null) {
|
if (termInst == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { content, prompt } = data || {}
|
const { content = [], prompt } = data || {}
|
||||||
if (!isEmpty(content)) {
|
if (!isEmpty(content)) {
|
||||||
termInst.write('\r\n' + content)
|
for (const line of content) {
|
||||||
|
termInst.write('\r\n' + line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!isEmpty(prompt)) {
|
if (!isEmpty(prompt)) {
|
||||||
promptPrefix.value = prompt
|
promptPrefix.value = prompt
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, reactive } from 'vue'
|
||||||
|
import { debounce, isEmpty, trim } from 'lodash'
|
||||||
|
import { NButton, NInput } from 'naive-ui'
|
||||||
|
|
||||||
|
const emit = defineEmits(['filterChanged', 'matchChanged'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {UnwrapNestedRefs<{filter: string, match: string}>}
|
||||||
|
*/
|
||||||
|
const inputData = reactive({
|
||||||
|
match: '',
|
||||||
|
filter: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasMatch = computed(() => {
|
||||||
|
return !isEmpty(trim(inputData.match))
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasFilter = computed(() => {
|
||||||
|
return !isEmpty(trim(inputData.filter))
|
||||||
|
})
|
||||||
|
|
||||||
|
const onFullSearch = () => {
|
||||||
|
inputData.filter = trim(inputData.filter)
|
||||||
|
if (!isEmpty(inputData.filter)) {
|
||||||
|
inputData.match = inputData.filter
|
||||||
|
inputData.filter = ''
|
||||||
|
emit('matchChanged', inputData.match, inputData.filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _onInput = () => {
|
||||||
|
emit('filterChanged', inputData.filter)
|
||||||
|
}
|
||||||
|
const onInput = debounce(_onInput, 500, { leading: true, trailing: true })
|
||||||
|
|
||||||
|
const onClearFilter = () => {
|
||||||
|
inputData.filter = ''
|
||||||
|
onClearMatch()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClearMatch = () => {
|
||||||
|
const changed = !isEmpty(inputData.match)
|
||||||
|
inputData.match = ''
|
||||||
|
if (changed) {
|
||||||
|
emit('matchChanged', inputData.match, inputData.filter)
|
||||||
|
} else {
|
||||||
|
emit('filterChanged', inputData.filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
reset: onClearFilter,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-input-group>
|
||||||
|
<n-input
|
||||||
|
v-model:value="inputData.filter"
|
||||||
|
:placeholder="$t('interface.filter')"
|
||||||
|
clearable
|
||||||
|
@clear="onClearFilter"
|
||||||
|
@input="onInput">
|
||||||
|
<template #prefix>
|
||||||
|
<n-tooltip v-if="hasMatch">
|
||||||
|
<template #trigger>
|
||||||
|
<n-tag closable size="small" @close="onClearMatch">
|
||||||
|
{{ inputData.match }}
|
||||||
|
</n-tag>
|
||||||
|
</template>
|
||||||
|
{{ $t('interface.full_search_result', { pattern: inputData.match }) }}
|
||||||
|
</n-tooltip>
|
||||||
|
</template>
|
||||||
|
</n-input>
|
||||||
|
<n-button :disabled="hasMatch && !hasFilter" :focusable="false" @click="onFullSearch">
|
||||||
|
{{ $t('interface.full_search') }}
|
||||||
|
</n-button>
|
||||||
|
</n-input-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ContentToolbar from './ContentToolbar.vue'
|
import ContentToolbar from './ContentToolbar.vue'
|
||||||
import AddLink from '@/components/icons/AddLink.vue'
|
import AddLink from '@/components/icons/AddLink.vue'
|
||||||
import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||||
import useDialogStore from 'stores/dialog.js'
|
import useDialogStore from 'stores/dialog.js'
|
||||||
|
@ -18,6 +18,7 @@ import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vu
|
||||||
import Edit from '@/components/icons/Edit.vue'
|
import Edit from '@/components/icons/Edit.vue'
|
||||||
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
||||||
import { decodeRedisKey } from '@/utils/key_convert.js'
|
import { decodeRedisKey } from '@/utils/key_convert.js'
|
||||||
|
import ContentSearchInput from '@/components/content_value/ContentSearchInput.vue'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
@ -52,7 +53,7 @@ const props = defineProps({
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -62,18 +63,6 @@ const keyName = computed(() => {
|
||||||
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterOption = [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: i18n.t('common.field'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: i18n.t('common.value'),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const filterType = ref(1)
|
|
||||||
|
|
||||||
const browserStore = useBrowserStore()
|
const browserStore = useBrowserStore()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const keyType = redisTypes.HASH
|
const keyType = redisTypes.HASH
|
||||||
|
@ -114,7 +103,7 @@ const fieldColumn = computed(() => ({
|
||||||
const displayCode = computed(() => {
|
const displayCode = computed(() => {
|
||||||
return props.format === formatTypes.JSON
|
return props.format === formatTypes.JSON
|
||||||
})
|
})
|
||||||
const valueFilterOption = ref(null)
|
// const valueFilterOption = ref(null)
|
||||||
const valueColumn = computed(() => ({
|
const valueColumn = computed(() => ({
|
||||||
key: 'value',
|
key: 'value',
|
||||||
title: i18n.t('common.value'),
|
title: i18n.t('common.value'),
|
||||||
|
@ -126,14 +115,14 @@ const valueColumn = computed(() => ({
|
||||||
: {
|
: {
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
},
|
},
|
||||||
filterOptionValue: valueFilterOption.value,
|
// filterOptionValue: valueFilterOption.value,
|
||||||
className: inEdit.value ? 'clickable' : '',
|
className: inEdit.value ? 'clickable' : '',
|
||||||
filter: (value, row) => {
|
// filter: (value, row) => {
|
||||||
if (row.dv) {
|
// if (row.dv) {
|
||||||
return !!~row.dv.indexOf(value.toString())
|
// return !!~row.dv.indexOf(value.toString())
|
||||||
}
|
// }
|
||||||
return !!~row.v.indexOf(value.toString())
|
// return !!~row.v.indexOf(value.toString())
|
||||||
},
|
// },
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
if (displayCode.value) {
|
if (displayCode.value) {
|
||||||
return h(NCode, { language: 'json', wordWrap: true, code: row.dv || row.v })
|
return h(NCode, { language: 'json', wordWrap: true, code: row.dv || row.v })
|
||||||
|
@ -283,50 +272,28 @@ const onAddRow = () => {
|
||||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.HASH)
|
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.HASH)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterValue = ref('')
|
|
||||||
const onFilterInput = (val) => {
|
const onFilterInput = (val) => {
|
||||||
switch (filterType.value) {
|
fieldFilterOption.value = val
|
||||||
case filterOption[0].value:
|
|
||||||
// filter field
|
|
||||||
valueFilterOption.value = null
|
|
||||||
fieldFilterOption.value = val
|
|
||||||
break
|
|
||||||
case filterOption[1].value:
|
|
||||||
// filter value
|
|
||||||
fieldFilterOption.value = null
|
|
||||||
valueFilterOption.value = val
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeFilterType = (type) => {
|
const onMatchInput = (matchVal, filterVal) => {
|
||||||
onFilterInput(filterValue.value)
|
fieldFilterOption.value = filterVal
|
||||||
}
|
emit('match', matchVal)
|
||||||
|
|
||||||
const clearFilter = () => {
|
|
||||||
fieldFilterOption.value = null
|
|
||||||
valueFilterOption.value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateFilter = (filters, sourceColumn) => {
|
const onUpdateFilter = (filters, sourceColumn) => {
|
||||||
switch (filterType.value) {
|
fieldFilterOption.value = filters[sourceColumn.key]
|
||||||
case filterOption[0].value:
|
|
||||||
fieldFilterOption.value = filters[sourceColumn.key]
|
|
||||||
break
|
|
||||||
case filterOption[1].value:
|
|
||||||
valueFilterOption.value = filters[sourceColumn.key]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFormatChanged = (selDecode, selFormat) => {
|
const onFormatChanged = (selDecode, selFormat) => {
|
||||||
emit('reload', selDecode, selFormat)
|
emit('reload', selDecode, selFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchInputRef = ref(null)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reset: () => {
|
reset: () => {
|
||||||
clearFilter()
|
|
||||||
resetEdit()
|
resetEdit()
|
||||||
|
searchInputRef.value?.reset()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -347,20 +314,10 @@ defineExpose({
|
||||||
@rename="emit('rename')" />
|
@rename="emit('rename')" />
|
||||||
<div class="tb2 value-item-part flex-box-h">
|
<div class="tb2 value-item-part flex-box-h">
|
||||||
<div class="flex-box-h">
|
<div class="flex-box-h">
|
||||||
<n-input-group>
|
<content-search-input
|
||||||
<n-select
|
ref="searchInputRef"
|
||||||
v-model:value="filterType"
|
@filter-changed="onFilterInput"
|
||||||
:consistent-menu-width="false"
|
@match-changed="onMatchInput" />
|
||||||
:options="filterOption"
|
|
||||||
style="width: 120px"
|
|
||||||
@update:value="onChangeFilterType" />
|
|
||||||
<n-input
|
|
||||||
v-model:value="filterValue"
|
|
||||||
:placeholder="$t('interface.search')"
|
|
||||||
clearable
|
|
||||||
@clear="clearFilter"
|
|
||||||
@update:value="onFilterInput" />
|
|
||||||
</n-input-group>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ContentToolbar from './ContentToolbar.vue'
|
import ContentToolbar from './ContentToolbar.vue'
|
||||||
import AddLink from '@/components/icons/AddLink.vue'
|
import AddLink from '@/components/icons/AddLink.vue'
|
||||||
import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { isEmpty, size } from 'lodash'
|
import { isEmpty, size } from 'lodash'
|
||||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||||
|
@ -17,6 +17,7 @@ import IconButton from '@/components/common/IconButton.vue'
|
||||||
import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue'
|
import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue'
|
||||||
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
||||||
import Edit from '@/components/icons/Edit.vue'
|
import Edit from '@/components/icons/Edit.vue'
|
||||||
|
import ContentSearchInput from '@/components/content_value/ContentSearchInput.vue'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
@ -52,7 +53,7 @@ const props = defineProps({
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -249,13 +250,13 @@ const onAddValue = (value) => {
|
||||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.LIST)
|
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.LIST)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterValue = ref('')
|
|
||||||
const onFilterInput = (val) => {
|
const onFilterInput = (val) => {
|
||||||
valueFilterOption.value = val
|
valueFilterOption.value = val
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearFilter = () => {
|
const onMatchInput = (matchVal, filterVal) => {
|
||||||
valueFilterOption.value = null
|
valueFilterOption.value = filterVal
|
||||||
|
emit('match', matchVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateFilter = (filters, sourceColumn) => {
|
const onUpdateFilter = (filters, sourceColumn) => {
|
||||||
|
@ -266,10 +267,11 @@ const onFormatChanged = (selDecode, selFormat) => {
|
||||||
emit('reload', selDecode, selFormat)
|
emit('reload', selDecode, selFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchInputRef = ref(null)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reset: () => {
|
reset: () => {
|
||||||
clearFilter()
|
|
||||||
resetEdit()
|
resetEdit()
|
||||||
|
searchInputRef.value?.reset()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -290,12 +292,10 @@ defineExpose({
|
||||||
@rename="emit('rename')" />
|
@rename="emit('rename')" />
|
||||||
<div class="tb2 value-item-part flex-box-h">
|
<div class="tb2 value-item-part flex-box-h">
|
||||||
<div class="flex-box-h">
|
<div class="flex-box-h">
|
||||||
<n-input
|
<content-search-input
|
||||||
v-model:value="filterValue"
|
ref="searchInputRef"
|
||||||
:placeholder="$t('interface.search')"
|
@filter-changed="onFilterInput"
|
||||||
clearable
|
@match-changed="onMatchInput" />
|
||||||
@clear="clearFilter"
|
|
||||||
@update:value="onFilterInput" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ContentToolbar from './ContentToolbar.vue'
|
import ContentToolbar from './ContentToolbar.vue'
|
||||||
import AddLink from '@/components/icons/AddLink.vue'
|
import AddLink from '@/components/icons/AddLink.vue'
|
||||||
import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { isEmpty, size } from 'lodash'
|
import { isEmpty, size } from 'lodash'
|
||||||
import useDialogStore from 'stores/dialog.js'
|
import useDialogStore from 'stores/dialog.js'
|
||||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
|
@ -17,6 +17,7 @@ import IconButton from '@/components/common/IconButton.vue'
|
||||||
import Edit from '@/components/icons/Edit.vue'
|
import Edit from '@/components/icons/Edit.vue'
|
||||||
import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue'
|
import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue'
|
||||||
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
||||||
|
import ContentSearchInput from '@/components/content_value/ContentSearchInput.vue'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
@ -51,7 +52,7 @@ const props = defineProps({
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -246,13 +247,13 @@ const onAddValue = (value) => {
|
||||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.SET)
|
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.SET)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterValue = ref('')
|
|
||||||
const onFilterInput = (val) => {
|
const onFilterInput = (val) => {
|
||||||
valueFilterOption.value = val
|
valueFilterOption.value = val
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearFilter = () => {
|
const onMatchInput = (matchVal, filterVal) => {
|
||||||
valueFilterOption.value = null
|
valueFilterOption.value = filterVal
|
||||||
|
emit('match', matchVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateFilter = (filters, sourceColumn) => {
|
const onUpdateFilter = (filters, sourceColumn) => {
|
||||||
|
@ -263,10 +264,11 @@ const onFormatChanged = (selDecode, selFormat) => {
|
||||||
emit('reload', selDecode, selFormat)
|
emit('reload', selDecode, selFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchInputRef = ref(null)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reset: () => {
|
reset: () => {
|
||||||
clearFilter()
|
|
||||||
resetEdit()
|
resetEdit()
|
||||||
|
searchInputRef.value?.reset()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -287,12 +289,10 @@ defineExpose({
|
||||||
@rename="emit('rename')" />
|
@rename="emit('rename')" />
|
||||||
<div class="tb2 value-item-part flex-box-h">
|
<div class="tb2 value-item-part flex-box-h">
|
||||||
<div class="flex-box-h">
|
<div class="flex-box-h">
|
||||||
<n-input
|
<content-search-input
|
||||||
v-model:value="filterValue"
|
ref="searchInputRef"
|
||||||
:placeholder="$t('interface.search')"
|
@filter-changed="onFilterInput"
|
||||||
clearable
|
@match-changed="onMatchInput" />
|
||||||
@clear="clearFilter"
|
|
||||||
@update:value="onFilterInput" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
|
|
|
@ -3,17 +3,18 @@ import { computed, h, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ContentToolbar from './ContentToolbar.vue'
|
import ContentToolbar from './ContentToolbar.vue'
|
||||||
import AddLink from '@/components/icons/AddLink.vue'
|
import AddLink from '@/components/icons/AddLink.vue'
|
||||||
import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||||
import useDialogStore from 'stores/dialog.js'
|
import useDialogStore from 'stores/dialog.js'
|
||||||
import { includes, isEmpty, keys, size, some, values } from 'lodash'
|
import { includes, isEmpty, size } from 'lodash'
|
||||||
import bytes from 'bytes'
|
import bytes from 'bytes'
|
||||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
import LoadList from '@/components/icons/LoadList.vue'
|
import LoadList from '@/components/icons/LoadList.vue'
|
||||||
import LoadAll from '@/components/icons/LoadAll.vue'
|
import LoadAll from '@/components/icons/LoadAll.vue'
|
||||||
import IconButton from '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
|
import ContentSearchInput from '@/components/content_value/ContentSearchInput.vue'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
@ -48,7 +49,7 @@ const props = defineProps({
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -57,30 +58,17 @@ const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
||||||
const keyName = computed(() => {
|
const keyName = computed(() => {
|
||||||
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterOption = [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: i18n.t('common.field'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: i18n.t('common.value'),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const filterType = ref(1)
|
const filterType = ref(1)
|
||||||
|
|
||||||
const browserStore = useBrowserStore()
|
const browserStore = useBrowserStore()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const keyType = redisTypes.STREAM
|
const keyType = redisTypes.STREAM
|
||||||
const idFilterOption = ref(null)
|
|
||||||
const idColumn = computed(() => ({
|
const idColumn = computed(() => ({
|
||||||
key: 'id',
|
key: 'id',
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
resizable: true,
|
resizable: true,
|
||||||
filterOptionValue: idFilterOption.value,
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const valueFilterOption = ref(null)
|
const valueFilterOption = ref(null)
|
||||||
|
@ -93,13 +81,15 @@ const valueColumn = computed(() => ({
|
||||||
filterOptionValue: valueFilterOption.value,
|
filterOptionValue: valueFilterOption.value,
|
||||||
filter: (value, row) => {
|
filter: (value, row) => {
|
||||||
const v = value.toString()
|
const v = value.toString()
|
||||||
if (filterType.value === 1) {
|
if (row.dv) {
|
||||||
// filter key
|
return includes(row.dv, v)
|
||||||
return some(keys(row.v), (key) => includes(key, v))
|
|
||||||
} else {
|
|
||||||
// filter value
|
|
||||||
return some(values(row.v), (val) => includes(val, v))
|
|
||||||
}
|
}
|
||||||
|
for (const k in row.v) {
|
||||||
|
if (includes(k, v) || includes(row.v[k], v)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
// sorter: (row1, row2) => row1.value - row2.value,
|
// sorter: (row1, row2) => row1.value - row2.value,
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
|
@ -148,34 +138,23 @@ const onAddRow = () => {
|
||||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.STREAM)
|
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.STREAM)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterValue = ref('')
|
|
||||||
const onFilterInput = (val) => {
|
const onFilterInput = (val) => {
|
||||||
valueFilterOption.value = val
|
valueFilterOption.value = val
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeFilterType = (type) => {
|
const onMatchInput = (matchVal, filterVal) => {
|
||||||
onFilterInput(filterValue.value)
|
valueFilterOption.value = filterVal
|
||||||
}
|
emit('match', matchVal)
|
||||||
|
|
||||||
const clearFilter = () => {
|
|
||||||
idFilterOption.value = null
|
|
||||||
valueFilterOption.value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateFilter = (filters, sourceColumn) => {
|
const onUpdateFilter = (filters, sourceColumn) => {
|
||||||
switch (filterType.value) {
|
valueFilterOption.value = filters[sourceColumn.key]
|
||||||
case filterOption[0].value:
|
|
||||||
idFilterOption.value = filters[sourceColumn.key]
|
|
||||||
break
|
|
||||||
case filterOption[1].value:
|
|
||||||
valueFilterOption.value = filters[sourceColumn.key]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchInputRef = ref(null)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reset: () => {
|
reset: () => {
|
||||||
clearFilter()
|
searchInputRef.value?.reset()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -196,20 +175,24 @@ defineExpose({
|
||||||
@rename="emit('rename')" />
|
@rename="emit('rename')" />
|
||||||
<div class="tb2 value-item-part flex-box-h">
|
<div class="tb2 value-item-part flex-box-h">
|
||||||
<div class="flex-box-h">
|
<div class="flex-box-h">
|
||||||
<n-input-group>
|
<!-- <n-input-group>-->
|
||||||
<n-select
|
<!-- <n-select-->
|
||||||
v-model:value="filterType"
|
<!-- v-model:value="filterType"-->
|
||||||
:consistent-menu-width="false"
|
<!-- :consistent-menu-width="false"-->
|
||||||
:options="filterOption"
|
<!-- :options="filterOption"-->
|
||||||
style="width: 120px"
|
<!-- style="width: 120px"-->
|
||||||
@update:value="onChangeFilterType" />
|
<!-- @update:value="onChangeFilterType" />-->
|
||||||
<n-input
|
<!-- <n-input-->
|
||||||
v-model:value="filterValue"
|
<!-- v-model:value="filterValue"-->
|
||||||
:placeholder="$t('interface.search')"
|
<!-- :placeholder="$t('interface.search')"-->
|
||||||
clearable
|
<!-- clearable-->
|
||||||
@clear="clearFilter"
|
<!-- @clear="clearFilter"-->
|
||||||
@update:value="onFilterInput" />
|
<!-- @update:value="onFilterInput" />-->
|
||||||
</n-input-group>
|
<!-- </n-input-group>-->
|
||||||
|
<content-search-input
|
||||||
|
ref="searchInputRef"
|
||||||
|
@filter-changed="onFilterInput"
|
||||||
|
@match-changed="onMatchInput" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
|
|
|
@ -64,7 +64,14 @@ const keyName = computed(() => {
|
||||||
return !isEmpty(data.value.keyCode) ? data.value.keyCode : data.value.keyPath
|
return !isEmpty(data.value.keyCode) ? data.value.keyCode : data.value.keyPath
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadData = async (reset, full) => {
|
/**
|
||||||
|
*
|
||||||
|
* @param {boolean} reset
|
||||||
|
* @param {boolean} [full]
|
||||||
|
* @param {string} [selMatch]
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
const loadData = async (reset, full, selMatch) => {
|
||||||
try {
|
try {
|
||||||
if (!!props.blank) {
|
if (!!props.blank) {
|
||||||
return
|
return
|
||||||
|
@ -75,7 +82,7 @@ const loadData = async (reset, full) => {
|
||||||
server: name,
|
server: name,
|
||||||
db: db,
|
db: db,
|
||||||
key: keyName.value,
|
key: keyName.value,
|
||||||
matchPattern: matchPattern,
|
matchPattern: selMatch === undefined ? matchPattern : selMatch,
|
||||||
decode: reset ? decodeTypes.NONE : decode,
|
decode: reset ? decodeTypes.NONE : decode,
|
||||||
format: reset ? formatTypes.RAW : format,
|
format: reset ? formatTypes.RAW : format,
|
||||||
reset,
|
reset,
|
||||||
|
@ -93,13 +100,14 @@ const loadData = async (reset, full) => {
|
||||||
*/
|
*/
|
||||||
const onReload = async (selDecode, selFormat) => {
|
const onReload = async (selDecode, selFormat) => {
|
||||||
try {
|
try {
|
||||||
const { name, db, keyCode, keyPath, decode, format } = data.value
|
const { name, db, keyCode, keyPath, decode, format, matchPattern } = data.value
|
||||||
await browserStore.reloadKey({
|
await browserStore.reloadKey({
|
||||||
server: name,
|
server: name,
|
||||||
db,
|
db,
|
||||||
key: keyCode || keyPath,
|
key: keyCode || keyPath,
|
||||||
decode: selDecode || decode,
|
decode: selDecode || decode,
|
||||||
format: selFormat || format,
|
format: selFormat || format,
|
||||||
|
matchPattern,
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
|
@ -133,6 +141,10 @@ const onLoadAll = () => {
|
||||||
loadData(false, true)
|
loadData(false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onMatch = (match) => {
|
||||||
|
loadData(true, false, match || '')
|
||||||
|
}
|
||||||
|
|
||||||
const contentRef = ref(null)
|
const contentRef = ref(null)
|
||||||
const initContent = async () => {
|
const initContent = async () => {
|
||||||
// onReload()
|
// onReload()
|
||||||
|
@ -141,7 +153,7 @@ const initContent = async () => {
|
||||||
if (contentRef.value?.reset != null) {
|
if (contentRef.value?.reset != null) {
|
||||||
contentRef.value?.reset()
|
contentRef.value?.reset()
|
||||||
}
|
}
|
||||||
await loadData(true, false)
|
await loadData(true, false, '')
|
||||||
if (contentRef.value?.beforeShow != null) {
|
if (contentRef.value?.beforeShow != null) {
|
||||||
await contentRef.value?.beforeShow()
|
await contentRef.value?.beforeShow()
|
||||||
}
|
}
|
||||||
|
@ -165,7 +177,7 @@ watch(() => data.value?.keyPath, initContent)
|
||||||
</template>
|
</template>
|
||||||
</n-empty>
|
</n-empty>
|
||||||
<!-- FIXME: keep alive may cause virtual list null value error. -->
|
<!-- FIXME: keep alive may cause virtual list null value error. -->
|
||||||
<!-- <keep-alive v-else>-->
|
<!-- <keep-alive v-else> -->
|
||||||
<component
|
<component
|
||||||
:is="valueComponents[data.type]"
|
:is="valueComponents[data.type]"
|
||||||
v-else
|
v-else
|
||||||
|
@ -185,6 +197,7 @@ watch(() => data.value?.keyPath, initContent)
|
||||||
@delete="onDelete"
|
@delete="onDelete"
|
||||||
@loadall="onLoadAll"
|
@loadall="onLoadAll"
|
||||||
@loadmore="onLoadMore"
|
@loadmore="onLoadMore"
|
||||||
|
@match="onMatch"
|
||||||
@reload="onReload"
|
@reload="onReload"
|
||||||
@rename="onRename" />
|
@rename="onRename" />
|
||||||
<!-- </keep-alive>-->
|
<!-- </keep-alive>-->
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ContentToolbar from './ContentToolbar.vue'
|
import ContentToolbar from './ContentToolbar.vue'
|
||||||
import AddLink from '@/components/icons/AddLink.vue'
|
import AddLink from '@/components/icons/AddLink.vue'
|
||||||
import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
|
import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
|
||||||
import { isEmpty, size } from 'lodash'
|
import { isEmpty, size } from 'lodash'
|
||||||
|
@ -17,6 +17,7 @@ import IconButton from '@/components/common/IconButton.vue'
|
||||||
import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue'
|
import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue'
|
||||||
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
import FormatSelector from '@/components/content_value/FormatSelector.vue'
|
||||||
import Edit from '@/components/icons/Edit.vue'
|
import Edit from '@/components/icons/Edit.vue'
|
||||||
|
import ContentSearchInput from '@/components/content_value/ContentSearchInput.vue'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
@ -51,7 +52,7 @@ const props = defineProps({
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete'])
|
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -61,18 +62,6 @@ const keyName = computed(() => {
|
||||||
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterOption = [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: i18n.t('common.value'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: i18n.t('common.score'),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const filterType = ref(1)
|
|
||||||
|
|
||||||
const browserStore = useBrowserStore()
|
const browserStore = useBrowserStore()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const keyType = redisTypes.ZSET
|
const keyType = redisTypes.ZSET
|
||||||
|
@ -89,46 +78,46 @@ const inEdit = computed(() => {
|
||||||
})
|
})
|
||||||
const fullEdit = ref(false)
|
const fullEdit = ref(false)
|
||||||
|
|
||||||
const scoreFilterOption = ref(null)
|
// const scoreFilterOption = ref(null)
|
||||||
const scoreColumn = computed(() => ({
|
const scoreColumn = computed(() => ({
|
||||||
key: 'score',
|
key: 'score',
|
||||||
title: i18n.t('common.score'),
|
title: i18n.t('common.score'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
resizable: true,
|
resizable: true,
|
||||||
filterOptionValue: scoreFilterOption.value,
|
// filterOptionValue: scoreFilterOption.value,
|
||||||
filter(value, row) {
|
// filter(value, row) {
|
||||||
const score = parseFloat(row.s)
|
// const score = parseFloat(row.s)
|
||||||
if (isNaN(score)) {
|
// if (isNaN(score)) {
|
||||||
return true
|
// return true
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
const regex = /^(>=|<=|>|<|=|!=)?(\d+(\.\d*)?)?$/
|
// const regex = /^(>=|<=|>|<|=|!=)?(\d+(\.\d*)?)?$/
|
||||||
const matches = value.match(regex)
|
// const matches = value.match(regex)
|
||||||
if (matches) {
|
// if (matches) {
|
||||||
const operator = matches[1] || ''
|
// const operator = matches[1] || ''
|
||||||
const filterScore = parseFloat(matches[2] || '')
|
// const filterScore = parseFloat(matches[2] || '')
|
||||||
if (!isNaN(filterScore)) {
|
// if (!isNaN(filterScore)) {
|
||||||
switch (operator) {
|
// switch (operator) {
|
||||||
case '>=':
|
// case '>=':
|
||||||
return score >= filterScore
|
// return score >= filterScore
|
||||||
case '<=':
|
// case '<=':
|
||||||
return score <= filterScore
|
// return score <= filterScore
|
||||||
case '>':
|
// case '>':
|
||||||
return score > filterScore
|
// return score > filterScore
|
||||||
case '<':
|
// case '<':
|
||||||
return score < filterScore
|
// return score < filterScore
|
||||||
case '=':
|
// case '=':
|
||||||
return score === filterScore
|
// return score === filterScore
|
||||||
case '!=':
|
// case '!=':
|
||||||
return score !== filterScore
|
// return score !== filterScore
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
return !!~row.v.indexOf(value.toString())
|
// return !!~row.v.indexOf(value.toString())
|
||||||
}
|
// }
|
||||||
return true
|
// return true
|
||||||
},
|
// },
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
return row.s
|
return row.s
|
||||||
},
|
},
|
||||||
|
@ -294,52 +283,28 @@ const onAddRow = () => {
|
||||||
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.ZSET)
|
dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.ZSET)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterValue = ref('')
|
|
||||||
const onFilterInput = (val) => {
|
const onFilterInput = (val) => {
|
||||||
switch (filterType.value) {
|
valueFilterOption.value = val
|
||||||
case filterOption[0].value:
|
|
||||||
// filter value
|
|
||||||
scoreFilterOption.value = null
|
|
||||||
valueFilterOption.value = val
|
|
||||||
break
|
|
||||||
case filterOption[1].value:
|
|
||||||
// filter score
|
|
||||||
valueFilterOption.value = null
|
|
||||||
scoreFilterOption.value = val
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeFilterType = (type) => {
|
const onMatchInput = (matchVal, filterVal) => {
|
||||||
onFilterInput(filterValue.value)
|
valueFilterOption.value = filterVal
|
||||||
}
|
emit('match', matchVal)
|
||||||
|
|
||||||
const clearFilter = () => {
|
|
||||||
valueFilterOption.value = null
|
|
||||||
scoreFilterOption.value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateFilter = (filters, sourceColumn) => {
|
const onUpdateFilter = (filters, sourceColumn) => {
|
||||||
switch (filterType.value) {
|
valueFilterOption.value = filters[sourceColumn.key]
|
||||||
case filterOption[0].value:
|
|
||||||
// filter value
|
|
||||||
valueFilterOption.value = filters[sourceColumn.key]
|
|
||||||
break
|
|
||||||
case filterOption[1].value:
|
|
||||||
// filter score
|
|
||||||
scoreFilterOption.value = filters[sourceColumn.key]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFormatChanged = (selDecode, selFormat) => {
|
const onFormatChanged = (selDecode, selFormat) => {
|
||||||
emit('reload', selDecode, selFormat)
|
emit('reload', selDecode, selFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchInputRef = ref(null)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reset: () => {
|
reset: () => {
|
||||||
clearFilter()
|
|
||||||
resetEdit()
|
resetEdit()
|
||||||
|
searchInputRef.value?.reset()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -360,25 +325,10 @@ defineExpose({
|
||||||
@rename="emit('rename')" />
|
@rename="emit('rename')" />
|
||||||
<div class="tb2 value-item-part flex-box-h">
|
<div class="tb2 value-item-part flex-box-h">
|
||||||
<div class="flex-box-h">
|
<div class="flex-box-h">
|
||||||
<n-input-group>
|
<content-search-input
|
||||||
<n-select
|
ref="searchInputRef"
|
||||||
v-model:value="filterType"
|
@filter-changed="onFilterInput"
|
||||||
:consistent-menu-width="false"
|
@match-changed="onMatchInput" />
|
||||||
:options="filterOption"
|
|
||||||
style="width: 120px"
|
|
||||||
@update:value="onChangeFilterType" />
|
|
||||||
<n-tooltip :delay="500" :disabled="filterType !== 2">
|
|
||||||
<template #trigger>
|
|
||||||
<n-input
|
|
||||||
v-model:value="filterValue"
|
|
||||||
:placeholder="$t('interface.search')"
|
|
||||||
clearable
|
|
||||||
@clear="clearFilter"
|
|
||||||
@update:value="onFilterInput" />
|
|
||||||
</template>
|
|
||||||
<div class="text-block">{{ $t('interface.score_filter_tip') }}</div>
|
|
||||||
</n-tooltip>
|
|
||||||
</n-input-group>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
|
|
|
@ -120,8 +120,10 @@ const onLoadSentinelMasters = async () => {
|
||||||
|
|
||||||
const tab = ref('general')
|
const tab = ref('general')
|
||||||
const testing = ref(false)
|
const testing = ref(false)
|
||||||
const showTestResult = ref(false)
|
const testResult = ref(null)
|
||||||
const testResult = ref('')
|
const showTestResult = computed(() => {
|
||||||
|
return !testing.value && testResult.value != null
|
||||||
|
})
|
||||||
const predefineColors = ref(['', '#F75B52', '#F7A234', '#F7CE33', '#4ECF60', '#348CF7', '#B270D3'])
|
const predefineColors = ref(['', '#F75B52', '#F7A234', '#F7CE33', '#4ECF60', '#348CF7', '#B270D3'])
|
||||||
const generalFormRef = ref(null)
|
const generalFormRef = ref(null)
|
||||||
const advanceFormRef = ref(null)
|
const advanceFormRef = ref(null)
|
||||||
|
@ -194,8 +196,7 @@ const resetForm = () => {
|
||||||
generalForm.value = connectionStore.newDefaultConnection()
|
generalForm.value = connectionStore.newDefaultConnection()
|
||||||
generalFormRef.value?.restoreValidation()
|
generalFormRef.value?.restoreValidation()
|
||||||
testing.value = false
|
testing.value = false
|
||||||
showTestResult.value = false
|
testResult.value = null
|
||||||
testResult.value = ''
|
|
||||||
tab.value = 'general'
|
tab.value = 'general'
|
||||||
loadingSentinelMaster.value = false
|
loadingSentinelMaster.value = false
|
||||||
}
|
}
|
||||||
|
@ -226,7 +227,6 @@ const onTestConnection = async () => {
|
||||||
result = e.message
|
result = e.message
|
||||||
} finally {
|
} finally {
|
||||||
testing.value = false
|
testing.value = false
|
||||||
showTestResult.value = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEmpty(result)) {
|
if (!isEmpty(result)) {
|
||||||
|
@ -566,7 +566,7 @@ const onClose = () => {
|
||||||
<!-- test result alert-->
|
<!-- test result alert-->
|
||||||
<n-alert
|
<n-alert
|
||||||
v-if="showTestResult"
|
v-if="showTestResult"
|
||||||
:on-close="() => (showTestResult = false)"
|
:on-close="() => (testResult = '')"
|
||||||
:title="isEmpty(testResult) ? '' : $t('dialogue.connection.test_fail')"
|
:title="isEmpty(testResult) ? '' : $t('dialogue.connection.test_fail')"
|
||||||
:type="isEmpty(testResult) ? 'success' : 'error'"
|
:type="isEmpty(testResult) ? 'success' : 'error'"
|
||||||
closable>
|
closable>
|
||||||
|
|
|
@ -81,6 +81,8 @@
|
||||||
"pin_edit": "Pin Editor(Do not close after save)",
|
"pin_edit": "Pin Editor(Do not close after save)",
|
||||||
"unpin_edit": "Cancel Pin",
|
"unpin_edit": "Cancel Pin",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
|
"full_search": "Full Search",
|
||||||
|
"full_search_result": "The content has been matched as '*{pattern}*'",
|
||||||
"filter_field": "Filter Field",
|
"filter_field": "Filter Field",
|
||||||
"filter_value": "Filter Value",
|
"filter_value": "Filter Value",
|
||||||
"length": "Length",
|
"length": "Length",
|
||||||
|
|
|
@ -81,6 +81,8 @@
|
||||||
"pin_edit": "固定编辑框(保存后不关闭)",
|
"pin_edit": "固定编辑框(保存后不关闭)",
|
||||||
"unpin_edit": "取消固定",
|
"unpin_edit": "取消固定",
|
||||||
"search": "搜索",
|
"search": "搜索",
|
||||||
|
"full_search": "全文匹配",
|
||||||
|
"full_search_result": "内容已匹配为 *{pattern}*",
|
||||||
"filter_field": "筛选字段",
|
"filter_field": "筛选字段",
|
||||||
"filter_value": "筛选值",
|
"filter_value": "筛选值",
|
||||||
"length": "长度",
|
"length": "长度",
|
||||||
|
|
|
@ -429,14 +429,15 @@ const useBrowserStore = defineStore('browser', {
|
||||||
* @param {string|number[]} key
|
* @param {string|number[]} key
|
||||||
* @param {string} [decode]
|
* @param {string} [decode]
|
||||||
* @param {string} [format]
|
* @param {string} [format]
|
||||||
|
* @param {string} [matchPattern]
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async reloadKey({ server, db, key, decode, format }) {
|
async reloadKey({ server, db, key, decode, format, matchPattern }) {
|
||||||
const tab = useTabStore()
|
const tab = useTabStore()
|
||||||
try {
|
try {
|
||||||
tab.updateLoading({ server, db, loading: true })
|
tab.updateLoading({ server, db, loading: true })
|
||||||
await this.loadKeySummary({ server, db, key })
|
await this.loadKeySummary({ server, db, key })
|
||||||
await this.loadKeyDetail({ server, db, key, decode, format, reset: true })
|
await this.loadKeyDetail({ server, db, key, decode, format, matchPattern, reset: true })
|
||||||
} finally {
|
} finally {
|
||||||
tab.updateLoading({ server, db, loading: false })
|
tab.updateLoading({ server, db, loading: false })
|
||||||
}
|
}
|
||||||
|
@ -470,7 +471,7 @@ const useBrowserStore = defineStore('browser', {
|
||||||
lite: true,
|
lite: true,
|
||||||
})
|
})
|
||||||
if (success) {
|
if (success) {
|
||||||
const { value, decode: retDecode, format: retFormat, end } = data
|
const { value, decode: retDecode, format: retFormat, match: retMatch, reset: retReset, end } = data
|
||||||
tab.updateValue({
|
tab.updateValue({
|
||||||
server,
|
server,
|
||||||
db,
|
db,
|
||||||
|
@ -478,7 +479,8 @@ const useBrowserStore = defineStore('browser', {
|
||||||
value,
|
value,
|
||||||
decode: retDecode,
|
decode: retDecode,
|
||||||
format: retFormat,
|
format: retFormat,
|
||||||
reset: reset || full === true,
|
reset: retReset,
|
||||||
|
matchPattern: retMatch || '',
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,6 +21,7 @@ const useTabStore = defineStore('tab', {
|
||||||
* @property {int} [ttl] ttl of current key
|
* @property {int} [ttl] ttl of current key
|
||||||
* @param {string} [decode]
|
* @param {string} [decode]
|
||||||
* @param {string} [format]
|
* @param {string} [format]
|
||||||
|
* @param {string} [matchPattern]
|
||||||
* @param {boolean} [end]
|
* @param {boolean} [end]
|
||||||
* @param {boolean} [loading]
|
* @param {boolean} [loading]
|
||||||
*/
|
*/
|
||||||
|
@ -159,9 +160,10 @@ const useTabStore = defineStore('tab', {
|
||||||
* @param {string} [keyCode]
|
* @param {string} [keyCode]
|
||||||
* @param {number} [size]
|
* @param {number} [size]
|
||||||
* @param {number} [length]
|
* @param {number} [length]
|
||||||
|
* @param {string} [matchPattern]
|
||||||
* @param {*} [value]
|
* @param {*} [value]
|
||||||
*/
|
*/
|
||||||
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, length }) {
|
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, length, matchPattern = '' }) {
|
||||||
let tabIndex = findIndex(this.tabList, { name: server })
|
let tabIndex = findIndex(this.tabList, { name: server })
|
||||||
if (tabIndex === -1) {
|
if (tabIndex === -1) {
|
||||||
this.tabList.push({
|
this.tabList.push({
|
||||||
|
@ -176,6 +178,7 @@ const useTabStore = defineStore('tab', {
|
||||||
keyCode,
|
keyCode,
|
||||||
size,
|
size,
|
||||||
length,
|
length,
|
||||||
|
matchPattern,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
})
|
})
|
||||||
tabIndex = this.tabList.length - 1
|
tabIndex = this.tabList.length - 1
|
||||||
|
@ -193,6 +196,7 @@ const useTabStore = defineStore('tab', {
|
||||||
tab.keyCode = keyCode
|
tab.keyCode = keyCode
|
||||||
tab.size = size
|
tab.size = size
|
||||||
tab.length = length
|
tab.length = length
|
||||||
|
tab.matchPattern = matchPattern
|
||||||
tab.value = undefined
|
tab.value = undefined
|
||||||
}
|
}
|
||||||
this._setActivatedIndex(tabIndex, true, subTab)
|
this._setActivatedIndex(tabIndex, true, subTab)
|
||||||
|
@ -207,29 +211,31 @@ const useTabStore = defineStore('tab', {
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @param {string} [format]
|
* @param {string} [format]
|
||||||
* @param {string] [decode]
|
* @param {string] [decode]
|
||||||
|
* @param {string} [matchPattern]
|
||||||
* @param {boolean} reset
|
* @param {boolean} reset
|
||||||
* @param {boolean} [end] keep end status if not set
|
* @param {boolean} [end] keep end status if not set
|
||||||
*/
|
*/
|
||||||
updateValue({ server, db, key, value, format, decode, reset, end }) {
|
updateValue({ server, db, key, value, format, decode, matchPattern, reset, end }) {
|
||||||
const tab = find(this.tabList, { name: server, db, key })
|
const tabData = find(this.tabList, { name: server, db, key })
|
||||||
if (tab == null) {
|
if (tabData == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.format = format || tab.format
|
tabData.format = format || tabData.format
|
||||||
tab.decode = decode || tab.decode
|
tabData.decode = decode || tabData.decode
|
||||||
|
tabData.matchPattern = matchPattern || ''
|
||||||
if (typeof end === 'boolean') {
|
if (typeof end === 'boolean') {
|
||||||
tab.end = end
|
tabData.end = end
|
||||||
}
|
}
|
||||||
if (!reset && typeof value === 'object') {
|
if (!reset && typeof value === 'object') {
|
||||||
if (value instanceof Array) {
|
if (value instanceof Array) {
|
||||||
tab.value = tab.value || []
|
tabData.value = tabData.value || []
|
||||||
tab.value.push(...value)
|
tabData.value.push(...value)
|
||||||
} else {
|
} else {
|
||||||
tab.value = assign(value, tab.value || {})
|
tabData.value = assign(value, tabData.value || {})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tab.value = value
|
tabData.value = value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue