feat: split "view as" to "view type" and "decode type" #60
feat: add footer to the value content page
This commit is contained in:
parent
d0af3deaeb
commit
30866d33f0
|
@ -791,7 +791,7 @@ func (c *connectionService) LoadAllKeys(connName string, db int, match, keyType
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKeyValue get value by key
|
// GetKeyValue get value by key
|
||||||
func (c *connectionService) GetKeyValue(connName string, db int, k any, viewAs string) (resp types.JSResp) {
|
func (c *connectionService) GetKeyValue(connName string, db int, k any, viewAs, decodeType string) (resp types.JSResp) {
|
||||||
item, err := c.getRedisClient(connName, db)
|
item, err := c.getRedisClient(connName, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
|
@ -831,7 +831,7 @@ func (c *connectionService) GetKeyValue(connName string, db int, k any, viewAs s
|
||||||
case "string":
|
case "string":
|
||||||
var str string
|
var str string
|
||||||
str, err = client.Get(ctx, key).Result()
|
str, err = client.Get(ctx, key).Result()
|
||||||
value, viewAs = strutil.ConvertTo(str, viewAs)
|
value, decodeType, viewAs = strutil.ConvertTo(str, decodeType, viewAs)
|
||||||
size, _ = client.StrLen(ctx, key).Result()
|
size, _ = client.StrLen(ctx, key).Result()
|
||||||
case "list":
|
case "list":
|
||||||
value, err = client.LRange(ctx, key, 0, -1).Result()
|
value, err = client.LRange(ctx, key, 0, -1).Result()
|
||||||
|
@ -928,13 +928,14 @@ func (c *connectionService) GetKeyValue(connName string, db int, k any, viewAs s
|
||||||
"value": value,
|
"value": value,
|
||||||
"size": size,
|
"size": size,
|
||||||
"viewAs": viewAs,
|
"viewAs": viewAs,
|
||||||
|
"decode": decodeType,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetKeyValue set value by key
|
// SetKeyValue set value by key
|
||||||
// @param ttl <= 0 means keep current ttl
|
// @param ttl <= 0 means keep current ttl
|
||||||
func (c *connectionService) SetKeyValue(connName string, db int, k any, keyType string, value any, ttl int64, viewAs string) (resp types.JSResp) {
|
func (c *connectionService) SetKeyValue(connName string, db int, k any, keyType string, value any, ttl int64, viewAs, decode string) (resp types.JSResp) {
|
||||||
item, err := c.getRedisClient(connName, db)
|
item, err := c.getRedisClient(connName, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
|
@ -958,7 +959,7 @@ func (c *connectionService) SetKeyValue(connName string, db int, k any, keyType
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
var saveStr string
|
var saveStr string
|
||||||
if saveStr, err = strutil.SaveAs(str, viewAs); err != nil {
|
if saveStr, err = strutil.SaveAs(str, viewAs, decode); err != nil {
|
||||||
resp.Msg = fmt.Sprintf(`save to "%s" type fail: %s`, viewAs, err.Error())
|
resp.Msg = fmt.Sprintf(`save to "%s" type fail: %s`, viewAs, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
const PLAIN_TEXT = "Plain Text"
|
const VIEWAS_PLAIN_TEXT = "Plain Text"
|
||||||
const JSON = "JSON"
|
const VIEWAS_JSON = "JSON"
|
||||||
const BASE64_TEXT = "Base64 Text"
|
const VIEWAS_HEX = "Hex"
|
||||||
const BASE64_JSON = "Base64 JSON"
|
const VIEWAS_BINARY = "Binary"
|
||||||
const HEX = "Hex"
|
|
||||||
const BINARY = "Binary"
|
const DECODE_NONE = "None"
|
||||||
const GZIP = "GZip"
|
const DECODE_BASE64 = "Base64"
|
||||||
const GZIP_JSON = "GZip JSON"
|
const DECODE_GZIP = "GZip"
|
||||||
const DEFLATE = "Deflate"
|
const DECODE_DEFLATE = "Deflate"
|
||||||
const DEFLATE_JSON = "Deflate JSON"
|
const DECODE_BROTLI = "Brotli"
|
||||||
const BROTLI = "Brotli"
|
|
||||||
const BROTLI_JSON = "Brotli JSON"
|
|
||||||
|
|
|
@ -18,160 +18,171 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertTo convert string to specified type
|
// ConvertTo convert string to specified type
|
||||||
// @param targetType empty string indicates automatic detection of the string type
|
// @param decodeType empty string indicates automatic detection
|
||||||
func ConvertTo(str, targetType string) (value, resultType string) {
|
// @param formatType empty string indicates automatic detection
|
||||||
|
func ConvertTo(str, decodeType, formatType string) (value, resultDecode, resultFormat string) {
|
||||||
if len(str) <= 0 {
|
if len(str) <= 0 {
|
||||||
// empty content
|
// empty content
|
||||||
if len(targetType) <= 0 {
|
if len(formatType) <= 0 {
|
||||||
resultType = types.PLAIN_TEXT
|
resultFormat = types.VIEWAS_PLAIN_TEXT
|
||||||
} else {
|
} else {
|
||||||
resultType = targetType
|
resultFormat = formatType
|
||||||
|
}
|
||||||
|
if len(decodeType) <= 0 {
|
||||||
|
resultDecode = types.DECODE_NONE
|
||||||
|
} else {
|
||||||
|
resultDecode = decodeType
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch targetType {
|
// decode first
|
||||||
case types.PLAIN_TEXT:
|
value, resultDecode = decodeWith(str, decodeType)
|
||||||
value = str
|
// then format content
|
||||||
resultType = targetType
|
value, resultFormat = viewAs(value, formatType)
|
||||||
return
|
return
|
||||||
|
|
||||||
case types.JSON:
|
|
||||||
value, _ = decodeJson(str)
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
|
|
||||||
case types.BASE64_TEXT, types.BASE64_JSON:
|
|
||||||
if base64Str, ok := decodeBase64(str); ok {
|
|
||||||
if targetType == types.BASE64_JSON {
|
|
||||||
value, _ = decodeJson(base64Str)
|
|
||||||
} else {
|
|
||||||
value = base64Str
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = str
|
|
||||||
}
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
|
|
||||||
case types.HEX:
|
|
||||||
if hexStr, ok := decodeToHex(str); ok {
|
|
||||||
value = hexStr
|
|
||||||
} else {
|
|
||||||
value = str
|
|
||||||
}
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
|
|
||||||
case types.BINARY:
|
|
||||||
if binStr, ok := decodeBinary(str); ok {
|
|
||||||
value = binStr
|
|
||||||
} else {
|
|
||||||
value = str
|
|
||||||
}
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
|
|
||||||
case types.GZIP, types.GZIP_JSON:
|
|
||||||
if gzipStr, ok := decodeGZip(str); ok {
|
|
||||||
if targetType == types.GZIP_JSON {
|
|
||||||
value, _ = decodeJson(gzipStr)
|
|
||||||
} else {
|
|
||||||
value = gzipStr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = str
|
|
||||||
}
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
|
|
||||||
case types.DEFLATE, types.DEFLATE_JSON:
|
|
||||||
if deflateStr, ok := decodeDeflate(str); ok {
|
|
||||||
if targetType == types.DEFLATE_JSON {
|
|
||||||
value, _ = decodeJson(deflateStr)
|
|
||||||
} else {
|
|
||||||
value = deflateStr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = str
|
|
||||||
}
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
|
|
||||||
case types.BROTLI, types.BROTLI_JSON:
|
|
||||||
if brotliStr, ok := decodeBrotli(str); ok {
|
|
||||||
if targetType == types.BROTLI_JSON {
|
|
||||||
value, _ = decodeJson(brotliStr)
|
|
||||||
} else {
|
|
||||||
value = brotliStr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = str
|
|
||||||
}
|
|
||||||
resultType = targetType
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// type isn't specified or unknown, try to automatically detect and return converted value
|
|
||||||
return autoToType(str)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt automatic convert to possible types
|
func decodeWith(str, decodeType string) (value, resultDecode string) {
|
||||||
// if no conversion is possible, it will return the origin string value and "plain text" type
|
if len(decodeType) > 0 {
|
||||||
func autoToType(str string) (value, resultType string) {
|
switch decodeType {
|
||||||
if len(str) > 0 {
|
case types.DECODE_NONE:
|
||||||
var ok bool
|
value = str
|
||||||
if value, ok = decodeJson(str); ok {
|
resultDecode = decodeType
|
||||||
resultType = types.JSON
|
return
|
||||||
|
|
||||||
|
case types.DECODE_BASE64:
|
||||||
|
if base64Str, ok := decodeBase64(str); ok {
|
||||||
|
value = base64Str
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
|
}
|
||||||
|
resultDecode = decodeType
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.DECODE_GZIP:
|
||||||
|
if gzipStr, ok := decodeGZip(str); ok {
|
||||||
|
value = gzipStr
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
|
}
|
||||||
|
resultDecode = decodeType
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.DECODE_DEFLATE:
|
||||||
|
if gzipStr, ok := decodeDeflate(str); ok {
|
||||||
|
value = gzipStr
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
|
}
|
||||||
|
resultDecode = decodeType
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.DECODE_BROTLI:
|
||||||
|
if gzipStr, ok := decodeBrotli(str); ok {
|
||||||
|
value = gzipStr
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
|
}
|
||||||
|
resultDecode = decodeType
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return autoDecode(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt try possible decode method
|
||||||
|
// if no decode is possible, it will return the origin string value and "none" decode type
|
||||||
|
func autoDecode(str string) (value, resultDecode string) {
|
||||||
|
if len(str) > 0 {
|
||||||
|
var ok bool
|
||||||
if value, ok = decodeBase64(str); ok {
|
if value, ok = decodeBase64(str); ok {
|
||||||
if value, ok = decodeJson(value); ok {
|
resultDecode = types.DECODE_BASE64
|
||||||
resultType = types.BASE64_JSON
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resultType = types.BASE64_TEXT
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok = decodeGZip(str); ok {
|
if value, ok = decodeGZip(str); ok {
|
||||||
if value, ok = decodeJson(value); ok {
|
resultDecode = types.DECODE_GZIP
|
||||||
resultType = types.GZIP_JSON
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resultType = types.GZIP
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok = decodeDeflate(str); ok {
|
if value, ok = decodeDeflate(str); ok {
|
||||||
if value, ok = decodeJson(value); ok {
|
resultDecode = types.DECODE_DEFLATE
|
||||||
resultType = types.DEFLATE_JSON
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resultType = types.DEFLATE
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok = decodeBrotli(str); ok {
|
if value, ok = decodeBrotli(str); ok {
|
||||||
if value, ok = decodeJson(value); ok {
|
resultDecode = types.DECODE_BROTLI
|
||||||
resultType = types.BROTLI_JSON
|
return
|
||||||
return
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = str
|
||||||
|
resultDecode = types.DECODE_NONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewAs(str, formatType string) (value, resultFormat string) {
|
||||||
|
if len(formatType) > 0 {
|
||||||
|
switch formatType {
|
||||||
|
case types.VIEWAS_PLAIN_TEXT:
|
||||||
|
value = str
|
||||||
|
resultFormat = formatType
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.VIEWAS_JSON:
|
||||||
|
if jsonStr, ok := decodeJson(str); ok {
|
||||||
|
value = jsonStr
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
}
|
}
|
||||||
resultType = types.BROTLI
|
resultFormat = formatType
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.VIEWAS_HEX:
|
||||||
|
if hexStr, ok := decodeToHex(str); ok {
|
||||||
|
value = hexStr
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
|
}
|
||||||
|
resultFormat = formatType
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.VIEWAS_BINARY:
|
||||||
|
if binStr, ok := decodeBinary(str); ok {
|
||||||
|
value = binStr
|
||||||
|
} else {
|
||||||
|
value = str
|
||||||
|
}
|
||||||
|
resultFormat = formatType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return autoViewAs(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt automatic convert to possible types
|
||||||
|
// if no conversion is possible, it will return the origin string value and "plain text" type
|
||||||
|
func autoViewAs(str string) (value, resultFormat string) {
|
||||||
|
if len(str) > 0 {
|
||||||
|
var ok bool
|
||||||
|
if value, ok = decodeJson(str); ok {
|
||||||
|
resultFormat = types.VIEWAS_JSON
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if containsBinary(str) {
|
if containsBinary(str) {
|
||||||
if value, ok = decodeToHex(str); ok {
|
if value, ok = decodeToHex(str); ok {
|
||||||
resultType = types.HEX
|
resultFormat = types.VIEWAS_HEX
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value = str
|
value = str
|
||||||
resultType = types.PLAIN_TEXT
|
resultFormat = types.VIEWAS_PLAIN_TEXT
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,65 +252,65 @@ func decodeBrotli(str string) (string, bool) {
|
||||||
return str, false
|
return str, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveAs(str, targetType string) (value string, err error) {
|
func SaveAs(str, viewType, decodeType string) (value string, err error) {
|
||||||
switch targetType {
|
value = str
|
||||||
case types.PLAIN_TEXT:
|
switch viewType {
|
||||||
return str, nil
|
case types.VIEWAS_JSON:
|
||||||
|
|
||||||
case types.BASE64_TEXT:
|
|
||||||
base64Str, _ := encodeBase64(str)
|
|
||||||
return base64Str, nil
|
|
||||||
|
|
||||||
case types.HEX:
|
|
||||||
hexStr, _ := encodeHex(str)
|
|
||||||
return hexStr, nil
|
|
||||||
|
|
||||||
case types.BINARY:
|
|
||||||
binStr, _ := encodeBinary(str)
|
|
||||||
return binStr, nil
|
|
||||||
|
|
||||||
case types.JSON, types.BASE64_JSON, types.GZIP_JSON, types.DEFLATE_JSON, types.BROTLI_JSON:
|
|
||||||
if jsonStr, ok := encodeJson(str); ok {
|
if jsonStr, ok := encodeJson(str); ok {
|
||||||
switch targetType {
|
value = jsonStr
|
||||||
case types.BASE64_JSON:
|
|
||||||
base64Str, _ := encodeBase64(jsonStr)
|
|
||||||
return base64Str, nil
|
|
||||||
case types.GZIP_JSON:
|
|
||||||
gzipStr, _ := encodeGZip(jsonStr)
|
|
||||||
return gzipStr, nil
|
|
||||||
case types.DEFLATE_JSON:
|
|
||||||
deflateStr, _ := encodeDeflate(jsonStr)
|
|
||||||
return deflateStr, nil
|
|
||||||
case types.BROTLI_JSON:
|
|
||||||
brotliStr, _ := encodeBrotli(jsonStr)
|
|
||||||
return brotliStr, nil
|
|
||||||
default:
|
|
||||||
return jsonStr, nil
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return str, errors.New("invalid json")
|
err = errors.New("invalid json data")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case types.GZIP:
|
case types.VIEWAS_HEX:
|
||||||
|
if hexStr, ok := encodeHex(str); ok {
|
||||||
|
value = hexStr
|
||||||
|
} else {
|
||||||
|
err = errors.New("invalid hex data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.VIEWAS_BINARY:
|
||||||
|
if binStr, ok := encodeBinary(str); ok {
|
||||||
|
value = binStr
|
||||||
|
} else {
|
||||||
|
err = errors.New("invalid binary data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch decodeType {
|
||||||
|
case types.DECODE_NONE:
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.DECODE_BASE64:
|
||||||
|
value, _ = encodeBase64(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
case types.DECODE_GZIP:
|
||||||
if gzipStr, ok := encodeGZip(str); ok {
|
if gzipStr, ok := encodeGZip(str); ok {
|
||||||
return gzipStr, nil
|
value = gzipStr
|
||||||
} else {
|
} else {
|
||||||
return str, errors.New("fail to build gzip data")
|
err = errors.New("fail to build gzip")
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
|
||||||
case types.DEFLATE:
|
case types.DECODE_DEFLATE:
|
||||||
if deflateStr, ok := encodeDeflate(str); ok {
|
if deflateStr, ok := encodeDeflate(str); ok {
|
||||||
return deflateStr, nil
|
value = deflateStr
|
||||||
} else {
|
} else {
|
||||||
return str, errors.New("fail to build deflate data")
|
err = errors.New("fail to build deflate")
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
|
||||||
case types.BROTLI:
|
case types.DECODE_BROTLI:
|
||||||
if brotliStr, ok := encodeBrotli(str); ok {
|
if brotliStr, ok := encodeBrotli(str); ok {
|
||||||
return brotliStr, nil
|
value = brotliStr
|
||||||
} else {
|
} else {
|
||||||
return str, errors.New("fail to build brotli data")
|
err = errors.New("fail to build brotli")
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return str, errors.New("fail to save with unknown error")
|
return str, errors.New("fail to save with unknown error")
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,8 +107,8 @@ onMounted(async () => {
|
||||||
<!-- app content-->
|
<!-- app content-->
|
||||||
<n-spin
|
<n-spin
|
||||||
:show="props.loading"
|
:show="props.loading"
|
||||||
:theme-overrides="{ opacitySpinning: 0 }"
|
:style="{ backgroundColor: themeVars.bodyColor }"
|
||||||
:style="{ backgroundColor: themeVars.bodyColor }">
|
:theme-overrides="{ opacitySpinning: 0 }">
|
||||||
<div id="app-content-wrapper" :style="wrapperStyle" class="flex-box-v">
|
<div id="app-content-wrapper" :style="wrapperStyle" class="flex-box-v">
|
||||||
<!-- title bar -->
|
<!-- title bar -->
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -34,11 +34,11 @@ const hasTooltip = computed(() => {
|
||||||
<n-tooltip v-if="hasTooltip" :show-arrow="false">
|
<n-tooltip v-if="hasTooltip" :show-arrow="false">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button
|
<n-button
|
||||||
|
:color="props.color"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:focusable="false"
|
:focusable="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:text="!border"
|
:text="!border"
|
||||||
:color="props.color"
|
|
||||||
@click.prevent="emit('click')">
|
@click.prevent="emit('click')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :color="props.color || 'currentColor'" :size="props.size">
|
<n-icon :color="props.color || 'currentColor'" :size="props.size">
|
||||||
|
@ -51,11 +51,11 @@ const hasTooltip = computed(() => {
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
<n-button
|
<n-button
|
||||||
v-else
|
v-else
|
||||||
|
:color="props.color"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:focusable="false"
|
:focusable="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:text="!border"
|
:text="!border"
|
||||||
:color="props.color"
|
|
||||||
@click.prevent="emit('click')">
|
@click.prevent="emit('click')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :color="props.color || 'currentColor'" :size="props.size">
|
<n-icon :color="props.color || 'currentColor'" :size="props.size">
|
||||||
|
|
|
@ -51,6 +51,7 @@ const tabContent = computed(() => {
|
||||||
value: tab.value,
|
value: tab.value,
|
||||||
size: tab.size || 0,
|
size: tab.size || 0,
|
||||||
viewAs: tab.viewAs,
|
viewAs: tab.viewAs,
|
||||||
|
decode: tab.decode,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -127,6 +128,7 @@ watch(
|
||||||
<content-value-wrapper
|
<content-value-wrapper
|
||||||
:blank="isBlankValue"
|
:blank="isBlankValue"
|
||||||
:db="tabContent.db"
|
:db="tabContent.db"
|
||||||
|
:decode="tabContent.decode"
|
||||||
:key-code="tabContent.keyCode"
|
:key-code="tabContent.keyCode"
|
||||||
:key-path="tabContent.keyPath"
|
:key-path="tabContent.keyPath"
|
||||||
:name="tabContent.name"
|
:name="tabContent.name"
|
||||||
|
|
|
@ -128,7 +128,7 @@ const onDeleteKey = () => {
|
||||||
</n-tooltip>
|
</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="onRenameKey" />
|
||||||
</n-button-group>
|
</n-button-group>
|
||||||
<n-tooltip>
|
<n-tooltip :show-arrow="false">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button :focusable="false" @click="onDeleteKey">
|
<n-button :focusable="false" @click="onDeleteKey">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ContentToolbar from './ContentToolbar.vue'
|
import ContentToolbar from './ContentToolbar.vue'
|
||||||
import Copy from '@/components/icons/Copy.vue'
|
import Copy from '@/components/icons/Copy.vue'
|
||||||
import Save from '@/components/icons/Save.vue'
|
import Save from '@/components/icons/Save.vue'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
import { types } from '@/consts/value_view_type.js'
|
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||||
import Close from '@/components/icons/Close.vue'
|
import Close from '@/components/icons/Close.vue'
|
||||||
import Edit from '@/components/icons/Edit.vue'
|
|
||||||
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
||||||
import { isEmpty, map, toLower } from 'lodash'
|
import { isEmpty, toLower } from 'lodash'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
|
import DropdownSelector from '@/components/content_value/DropdownSelector.vue'
|
||||||
|
import Code from '@/components/icons/Code.vue'
|
||||||
|
import Conversion from '@/components/icons/Conversion.vue'
|
||||||
|
import EditFile from '@/components/icons/EditFile.vue'
|
||||||
|
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
@ -32,7 +35,11 @@ const props = defineProps({
|
||||||
size: Number,
|
size: Number,
|
||||||
viewAs: {
|
viewAs: {
|
||||||
type: String,
|
type: String,
|
||||||
default: types.PLAIN_TEXT,
|
default: formatTypes.PLAIN_TEXT,
|
||||||
|
},
|
||||||
|
decode: {
|
||||||
|
type: String,
|
||||||
|
default: decodeTypes.NONE,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -44,43 +51,20 @@ const keyName = computed(() => {
|
||||||
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath
|
||||||
})
|
})
|
||||||
|
|
||||||
const viewOption = computed(() =>
|
// const viewOption = computed(() =>
|
||||||
map(types, (t) => {
|
// map(types, (t) => {
|
||||||
return {
|
// return {
|
||||||
value: t,
|
// value: t,
|
||||||
label: t,
|
// label: t,
|
||||||
}
|
// key: t,
|
||||||
}),
|
// }
|
||||||
)
|
// }),
|
||||||
// const viewAs = ref(types.PLAIN_TEXT)
|
// )
|
||||||
|
|
||||||
const autoDetectFormat = () => {
|
|
||||||
// auto check format when loaded
|
|
||||||
// if (IsJson(props.value)) {
|
|
||||||
// viewAs.value = types.JSON
|
|
||||||
// } else {
|
|
||||||
// viewAs.value = types.PLAIN_TEXT
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
autoDetectFormat()
|
|
||||||
})
|
|
||||||
watch(
|
|
||||||
() => props.value,
|
|
||||||
(value) => {
|
|
||||||
autoDetectFormat()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const keyType = redisTypes.STRING
|
const keyType = redisTypes.STRING
|
||||||
const viewLanguage = computed(() => {
|
const viewLanguage = computed(() => {
|
||||||
switch (props.viewAs) {
|
switch (props.viewAs) {
|
||||||
case types.JSON:
|
case formatTypes.JSON:
|
||||||
case types.BASE64_JSON:
|
|
||||||
case types.GZIP_JSON:
|
|
||||||
case types.DEFLATE_JSON:
|
|
||||||
case types.BROTLI_JSON:
|
|
||||||
return 'json'
|
return 'json'
|
||||||
default:
|
default:
|
||||||
return 'plaintext'
|
return 'plaintext'
|
||||||
|
@ -88,7 +72,11 @@ const viewLanguage = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const onViewTypeUpdate = (viewType) => {
|
const onViewTypeUpdate = (viewType) => {
|
||||||
connectionStore.loadKeyValue(props.name, props.db, keyName.value, viewType)
|
connectionStore.loadKeyValue(props.name, props.db, keyName.value, viewType, props.decode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDecodeTypeUpdate = (decodeType) => {
|
||||||
|
connectionStore.loadKeyValue(props.name, props.db, keyName.value, props.viewAs, decodeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,6 +121,7 @@ const onSaveValue = async () => {
|
||||||
editValue.value,
|
editValue.value,
|
||||||
-1,
|
-1,
|
||||||
props.viewAs,
|
props.viewAs,
|
||||||
|
props.decode,
|
||||||
)
|
)
|
||||||
if (success) {
|
if (success) {
|
||||||
await connectionStore.loadKeyValue(props.name, props.db, keyName.value)
|
await connectionStore.loadKeyValue(props.name, props.db, keyName.value)
|
||||||
|
@ -157,15 +146,9 @@ const onSaveValue = async () => {
|
||||||
:key-path="keyPath"
|
:key-path="keyPath"
|
||||||
:key-type="keyType"
|
:key-type="keyType"
|
||||||
:server="props.name"
|
:server="props.name"
|
||||||
:ttl="ttl" />
|
:ttl="ttl"
|
||||||
<div class="tb2 flex-box-h">
|
class="value-item-part" />
|
||||||
<n-text>{{ $t('interface.view_as') }}</n-text>
|
<div class="tb2 value-item-part flex-box-h">
|
||||||
<n-select
|
|
||||||
:options="viewOption"
|
|
||||||
:value="props.viewAs"
|
|
||||||
filterable
|
|
||||||
style="width: 160px"
|
|
||||||
@update:value="onViewTypeUpdate" />
|
|
||||||
<div class="flex-item-expand"></div>
|
<div class="flex-item-expand"></div>
|
||||||
<n-button-group v-if="!inEdit">
|
<n-button-group v-if="!inEdit">
|
||||||
<n-button :focusable="false" @click="onCopyValue">
|
<n-button :focusable="false" @click="onCopyValue">
|
||||||
|
@ -176,7 +159,7 @@ const onSaveValue = async () => {
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button :focusable="false" plain @click="onEditValue">
|
<n-button :focusable="false" plain @click="onEditValue">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :component="Edit" size="18" />
|
<n-icon :component="EditFile" size="18" />
|
||||||
</template>
|
</template>
|
||||||
{{ $t('interface.edit_value') }}
|
{{ $t('interface.edit_value') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
|
@ -196,7 +179,7 @@ const onSaveValue = async () => {
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-button-group>
|
</n-button-group>
|
||||||
</div>
|
</div>
|
||||||
<div class="value-wrapper flex-item-expand flex-box-v">
|
<div class="value-wrapper value-item-part flex-item-expand flex-box-v">
|
||||||
<n-scrollbar v-if="!inEdit" class="flex-item-expand">
|
<n-scrollbar v-if="!inEdit" class="flex-item-expand">
|
||||||
<n-code :code="props.value" :language="viewLanguage" show-line-numbers style="cursor: text" word-wrap />
|
<n-code :code="props.value" :language="viewLanguage" show-line-numbers style="cursor: text" word-wrap />
|
||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
|
@ -208,6 +191,24 @@ const onSaveValue = async () => {
|
||||||
class="flex-item-expand"
|
class="flex-item-expand"
|
||||||
type="textarea" />
|
type="textarea" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="value-footer flex-box-h">
|
||||||
|
<div class="flex-item-expand"></div>
|
||||||
|
<dropdown-selector
|
||||||
|
:icon="Code"
|
||||||
|
:options="formatTypes"
|
||||||
|
:tooltip="$t('interface.view_as')"
|
||||||
|
:value="props.viewAs"
|
||||||
|
@update:value="onViewTypeUpdate" />
|
||||||
|
|
||||||
|
<n-divider vertical />
|
||||||
|
|
||||||
|
<dropdown-selector
|
||||||
|
:icon="Conversion"
|
||||||
|
:options="decodeTypes"
|
||||||
|
:tooltip="$t('interface.decode_with')"
|
||||||
|
:value="props.decode"
|
||||||
|
@update:value="onDecodeTypeUpdate" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -217,4 +218,9 @@ const onSaveValue = async () => {
|
||||||
border-top: v-bind('themeVars.borderColor') 1px solid;
|
border-top: v-bind('themeVars.borderColor') 1px solid;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.value-footer {
|
||||||
|
border-top: v-bind('themeVars.borderColor') 1px solid;
|
||||||
|
background-color: v-bind('themeVars.bodyColor');
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { types } from '@/consts/value_view_type.js'
|
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||||
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
import ContentValueString from '@/components/content_value/ContentValueString.vue'
|
import ContentValueString from '@/components/content_value/ContentValueString.vue'
|
||||||
import ContentValueHash from '@/components/content_value/ContentValueHash.vue'
|
import ContentValueHash from '@/components/content_value/ContentValueHash.vue'
|
||||||
|
@ -31,7 +31,11 @@ const props = defineProps({
|
||||||
size: Number,
|
size: Number,
|
||||||
viewAs: {
|
viewAs: {
|
||||||
type: String,
|
type: String,
|
||||||
default: types.PLAIN_TEXT,
|
default: formatTypes.PLAIN_TEXT,
|
||||||
|
},
|
||||||
|
decode: {
|
||||||
|
type: String,
|
||||||
|
default: decodeTypes.NONE,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -63,6 +67,7 @@ const onReloadKey = async () => {
|
||||||
<component
|
<component
|
||||||
:is="valueComponents[props.type]"
|
:is="valueComponents[props.type]"
|
||||||
:db="props.db"
|
:db="props.db"
|
||||||
|
:decode="props.decode"
|
||||||
:key-code="props.keyCode"
|
:key-code="props.keyCode"
|
||||||
:key-path="props.keyPath"
|
:key-path="props.keyPath"
|
||||||
:name="props.name"
|
:name="props.name"
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, h, ref } from 'vue'
|
||||||
|
import { map } from 'lodash'
|
||||||
|
import { NIcon, NText } from 'naive-ui'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object,
|
||||||
|
value: {},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
icon: [String, Object],
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:value'])
|
||||||
|
|
||||||
|
const renderHeader = () => {
|
||||||
|
return h('div', { class: 'type-selector-header' }, [h(NText, null, () => props.tooltip)])
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderLabel = (option) => {
|
||||||
|
return h('div', { class: 'type-selector-item' }, option.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dropdownOption = computed(() => {
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
key: 'header',
|
||||||
|
type: 'render',
|
||||||
|
render: renderHeader,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'header-divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return [
|
||||||
|
...options,
|
||||||
|
...map(props.options, (t) => {
|
||||||
|
return {
|
||||||
|
key: t,
|
||||||
|
label: t,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const onDropdownSelect = (key) => {
|
||||||
|
emit('update:value', key)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonText = computed(() => {
|
||||||
|
return props.value || get(dropdownOption.value, [1, 'label'], 'None')
|
||||||
|
})
|
||||||
|
|
||||||
|
const showDropdown = ref(false)
|
||||||
|
const onDropdownShow = (show) => {
|
||||||
|
showDropdown.value = show === true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-dropdown
|
||||||
|
:options="dropdownOption"
|
||||||
|
:render-label="renderLabel"
|
||||||
|
:show-arrow="true"
|
||||||
|
:title="props.tooltip"
|
||||||
|
:value="props.value"
|
||||||
|
trigger="click"
|
||||||
|
@select="onDropdownSelect"
|
||||||
|
@update:show="onDropdownShow">
|
||||||
|
<n-tooltip :disabled="showDropdown" :show-arrow="false">
|
||||||
|
{{ props.tooltip }}
|
||||||
|
<template #trigger>
|
||||||
|
<n-button :focusable="false" quaternary>
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<component :is="icon" />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ buttonText }}
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
</n-tooltip>
|
||||||
|
</n-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, h, reactive, ref, watch } from 'vue'
|
import { computed, h, reactive, ref, watch } from 'vue'
|
||||||
import { types, typesColor } from '@/consts/support_redis_type.js'
|
import { types, typesColor } from '@/consts/support_redis_type.js'
|
||||||
import { types as viewTypes } from '@/consts/value_view_type.js'
|
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||||
import useDialog from 'stores/dialog'
|
import useDialog from 'stores/dialog'
|
||||||
import { isEmpty, keys, map } from 'lodash'
|
import { isEmpty, keys, map } from 'lodash'
|
||||||
import NewStringValue from '@/components/new_value/NewStringValue.vue'
|
import NewStringValue from '@/components/new_value/NewStringValue.vue'
|
||||||
|
@ -124,7 +124,8 @@ const onAdd = async () => {
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
ttl,
|
ttl,
|
||||||
viewTypes.PLAIN_TEXT,
|
formatTypes.PLAIN_TEXT,
|
||||||
|
decodeTypes.NONE,
|
||||||
)
|
)
|
||||||
if (success) {
|
if (success) {
|
||||||
// select current key
|
// select current key
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M40 23V14L31 4H10C8.89543 4 8 4.89543 8 6V42C8 43.1046 8.89543 44 10 44H22"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M37 31L42 36L37 41"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M31 31L26 36L31 41"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M30 4V14H40"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M40 23V14L31 4H10C8.89543 4 8 4.89543 8 6V42C8 43.1046 8.89543 44 10 44H22"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M27 33H41"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M27 39H41"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M41 33L36 28"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M32 44L27 39"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M30 4V14H40"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -12,7 +12,7 @@ const props = defineProps({
|
||||||
<path
|
<path
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M7 42H43"
|
d="M7 42H43"
|
||||||
stroke="currentColorr"
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M40 23V14L31 4H10C8.89543 4 8 4.89543 8 6V42C8 43.1046 8.89543 44 10 44H22"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M32 44L42 34L38 30L28 40V44H32Z"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
d="M30 4V14H40"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -1,18 +1,24 @@
|
||||||
/**
|
/**
|
||||||
* string view mode
|
* string format types
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
export const types = {
|
export const formatTypes = {
|
||||||
PLAIN_TEXT: 'Plain Text',
|
PLAIN_TEXT: 'Plain Text',
|
||||||
JSON: 'JSON',
|
JSON: 'JSON',
|
||||||
BASE64_TEXT: 'Base64 Text',
|
|
||||||
BASE64_JSON: 'Base64 JSON',
|
|
||||||
HEX: 'Hex',
|
HEX: 'Hex',
|
||||||
BINARY: 'Binary',
|
BINARY: 'Binary',
|
||||||
GZIP: 'GZip',
|
}
|
||||||
GZIP_JSON: 'GZip JSON',
|
|
||||||
DEFLATE: 'Deflate',
|
/**
|
||||||
DEFLATE_JSON: 'Deflate JSON',
|
* string decode types
|
||||||
BROTLI: 'Brotli',
|
* @enum {string}
|
||||||
BROTLI_JSON: 'Brotli JSON',
|
*/
|
||||||
|
export const decodeTypes = {
|
||||||
|
NONE: 'None',
|
||||||
|
BASE64: 'Base64',
|
||||||
|
GZIP: 'GZip',
|
||||||
|
DEFLATE: 'Deflate',
|
||||||
|
BROTLI: 'Brotli',
|
||||||
|
// PHP: 'PHP',
|
||||||
|
// Java: 'Java',
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
"filter_field": "Filter Field",
|
"filter_field": "Filter Field",
|
||||||
"filter_value": "Filter Value",
|
"filter_value": "Filter Value",
|
||||||
"view_as": "View As",
|
"view_as": "View As",
|
||||||
|
"decode_with": "Decode/Decompress Type",
|
||||||
"reload": "Reload",
|
"reload": "Reload",
|
||||||
"open_connection": "Open Connection",
|
"open_connection": "Open Connection",
|
||||||
"batch_delete": "Batch Delete",
|
"batch_delete": "Batch Delete",
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
"filter_field": "筛选字段",
|
"filter_field": "筛选字段",
|
||||||
"filter_value": "筛选值",
|
"filter_value": "筛选值",
|
||||||
"view_as": "查看方式",
|
"view_as": "查看方式",
|
||||||
|
"decode_with": "解码/解压方式",
|
||||||
"reload": "重新载入",
|
"reload": "重新载入",
|
||||||
"open_connection": "打开连接",
|
"open_connection": "打开连接",
|
||||||
"batch_delete": "批量删除键",
|
"batch_delete": "批量删除键",
|
||||||
|
|
|
@ -657,14 +657,15 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @param {number} db
|
* @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|number[]} [key] when key is null or blank, update tab to display normal content (blank content or server status)
|
||||||
* @param {string} [viewType]
|
* @param {string} [viewType]
|
||||||
|
* @param {string} [decodeType]
|
||||||
*/
|
*/
|
||||||
async loadKeyValue(server, db, key, viewType) {
|
async loadKeyValue(server, db, key, viewType, decodeType) {
|
||||||
try {
|
try {
|
||||||
const tab = useTabStore()
|
const tab = useTabStore()
|
||||||
if (!isEmpty(key)) {
|
if (!isEmpty(key)) {
|
||||||
const { data, success, msg } = await GetKeyValue(server, db, key, viewType)
|
const { data, success, msg } = await GetKeyValue(server, db, key, viewType, decodeType)
|
||||||
if (success) {
|
if (success) {
|
||||||
const { type, ttl, value, size, viewAs } = data
|
const { type, ttl, value, size, viewAs, decode } = data
|
||||||
const k = decodeRedisKey(key)
|
const k = decodeRedisKey(key)
|
||||||
const binaryKey = k !== key
|
const binaryKey = k !== key
|
||||||
tab.upsertTab({
|
tab.upsertTab({
|
||||||
|
@ -678,6 +679,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
value,
|
value,
|
||||||
size,
|
size,
|
||||||
viewAs,
|
viewAs,
|
||||||
|
decode,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
@ -1092,11 +1094,12 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @param {any} value
|
* @param {any} value
|
||||||
* @param {number} ttl
|
* @param {number} ttl
|
||||||
* @param {string} [viewAs]
|
* @param {string} [viewAs]
|
||||||
|
* @param {string} [decode]
|
||||||
* @returns {Promise<{[msg]: string, success: boolean, [nodeKey]: {string}}>}
|
* @returns {Promise<{[msg]: string, success: boolean, [nodeKey]: {string}}>}
|
||||||
*/
|
*/
|
||||||
async setKey(connName, db, key, keyType, value, ttl, viewAs) {
|
async setKey(connName, db, key, keyType, value, ttl, viewAs, decode) {
|
||||||
try {
|
try {
|
||||||
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl, viewAs)
|
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl, viewAs, decode)
|
||||||
if (success) {
|
if (success) {
|
||||||
// update tree view data
|
// update tree view data
|
||||||
const { newKey = 0 } = this._addKeyNodes(connName, db, [key], true)
|
const { newKey = 0 } = this._addKeyNodes(connName, db, [key], true)
|
||||||
|
|
|
@ -94,8 +94,9 @@ const useTabStore = defineStore('tab', {
|
||||||
* @param {number} [size]
|
* @param {number} [size]
|
||||||
* @param {*} [value]
|
* @param {*} [value]
|
||||||
* @param {string} [viewAs]
|
* @param {string} [viewAs]
|
||||||
|
* @param {string} [decode]
|
||||||
*/
|
*/
|
||||||
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, value, viewAs }) {
|
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, value, viewAs, decode }) {
|
||||||
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({
|
||||||
|
@ -111,6 +112,7 @@ const useTabStore = defineStore('tab', {
|
||||||
size,
|
size,
|
||||||
value,
|
value,
|
||||||
viewAs,
|
viewAs,
|
||||||
|
decode,
|
||||||
})
|
})
|
||||||
tabIndex = this.tabList.length - 1
|
tabIndex = this.tabList.length - 1
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,6 +130,7 @@ const useTabStore = defineStore('tab', {
|
||||||
tab.size = size
|
tab.size = size
|
||||||
tab.value = value
|
tab.value = value
|
||||||
tab.viewAs = viewAs
|
tab.viewAs = viewAs
|
||||||
|
tab.decode = decode
|
||||||
}
|
}
|
||||||
this._setActivatedIndex(tabIndex, true, subTab)
|
this._setActivatedIndex(tabIndex, true, subTab)
|
||||||
// this.activatedTab = tab.name
|
// this.activatedTab = tab.name
|
||||||
|
|
|
@ -79,7 +79,8 @@ body {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
padding: 5px;
|
padding-top: 5px;
|
||||||
|
//padding: 5px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
.tb2 {
|
.tb2 {
|
||||||
|
@ -97,6 +98,17 @@ body {
|
||||||
user-select: text;
|
user-select: text;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.value-item-part {
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-footer {
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
padding: 3px 10px 3px 10px;
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.n-dynamic-input-item {
|
.n-dynamic-input-item {
|
||||||
|
@ -113,14 +125,27 @@ body {
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.type-selector-header {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-selector-item {
|
||||||
|
min-width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-pane-container {
|
.nav-pane-container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.nav-pane-bottom {
|
.nav-pane-bottom {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 3px 5px 5px 5px;
|
padding: 3px 10px 3px 10px;
|
||||||
min-height: 35px;
|
min-height: 30px;
|
||||||
//border-top: v-bind('themeVars.borderColor') 1px solid;
|
//border-top: v-bind('themeVars.borderColor') 1px solid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,15 @@ export const themeOverrides = {
|
||||||
scrollbarWidth: '8px',
|
scrollbarWidth: '8px',
|
||||||
tabColor: '#FFFFFF',
|
tabColor: '#FFFFFF',
|
||||||
},
|
},
|
||||||
|
Button: {
|
||||||
|
heightMedium: '32px',
|
||||||
|
},
|
||||||
Tag: {
|
Tag: {
|
||||||
// borderRadius: '3px'
|
// borderRadius: '3px'
|
||||||
|
heightLarge: '32px',
|
||||||
|
},
|
||||||
|
Input: {
|
||||||
|
heightMedium: '32px',
|
||||||
},
|
},
|
||||||
Tabs: {
|
Tabs: {
|
||||||
tabGapSmallCard: '2px',
|
tabGapSmallCard: '2px',
|
||||||
|
|
Loading…
Reference in New Issue