From fea87662dec83fc78f67111e98593bdb89e9eab6 Mon Sep 17 00:00:00 2001 From: Lykin <137850705+tiny-craft@users.noreply.github.com> Date: Wed, 15 Nov 2023 23:41:53 +0800 Subject: [PATCH] perf: sorted set value support format viewing #65 --- backend/services/browser_service.go | 145 ++++++---- backend/types/js_resp.go | 11 + backend/types/redis_wrapper.go | 9 +- backend/utils/string/convert.go | 27 +- .../content_value/ContentEntryEditor.vue | 5 +- .../content_value/ContentValueHash.vue | 5 +- .../content_value/ContentValueList.vue | 13 +- .../content_value/ContentValueSet.vue | 12 +- .../content_value/ContentValueWrapper.vue | 7 +- .../content_value/ContentValueZSet.vue | 260 +++++++++++------- .../components/dialogs/AddFieldsDialog.vue | 4 +- frontend/src/main.js | 6 +- frontend/src/stores/browser.js | 142 ++++++---- 13 files changed, 382 insertions(+), 264 deletions(-) diff --git a/backend/services/browser_service.go b/backend/services/browser_service.go index dd2aa82..ae27961 100644 --- a/backend/services/browser_service.go +++ b/backend/services/browser_service.go @@ -681,12 +681,11 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS case "hash": loadHashHandle := func() ([]types.HashEntryItem, bool, error) { - //items := map[string]string{} - scanSize := int64(Preferences().GetScanSize()) - items := make([]types.HashEntryItem, 0, scanSize) + var items []types.HashEntryItem var loadedVal []string var cursor uint64 var subErr error + scanSize := int64(Preferences().GetScanSize()) if param.Full { // load all cursor = 0 @@ -702,7 +701,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS }) if doConvert { if dv, _, _ := strutil.ConvertTo(loadedVal[i+1], param.Decode, param.Format); dv != loadedVal[i+1] { - items[i/2].DisplayValue = dv + items[len(items)-1].DisplayValue = dv } } } @@ -716,11 +715,11 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS if subErr != nil { return nil, false, subErr } - for i := 0; i < len(loadedVal); i += 2 { - items = append(items, types.HashEntryItem{ - Key: loadedVal[i], - Value: strutil.EncodeRedisKey(loadedVal[i+1]), - }) + loadedLen := len(loadedVal) + items = make([]types.HashEntryItem, loadedLen/2) + for i := 0; i < loadedLen; i += 2 { + items[i/2].Key = loadedVal[i] + items[i/2].Value = strutil.EncodeRedisKey(loadedVal[i+1]) if doConvert { if dv, _, _ := strutil.ConvertTo(loadedVal[i+1], param.Decode, param.Format); dv != loadedVal[i+1] { items[i/2].DisplayValue = dv @@ -744,8 +743,8 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS var items []types.SetEntryItem var cursor uint64 var subErr error - scanSize := int64(Preferences().GetScanSize()) var loadedKey []string + scanSize := int64(Preferences().GetScanSize()) if param.Full { // load all cursor = 0 @@ -754,6 +753,16 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS if subErr != nil { return items, false, subErr } + for _, val := range loadedKey { + items = append(items, types.SetEntryItem{ + Value: val, + }) + if doConvert { + if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val { + items[len(items)-1].DisplayValue = dv + } + } + } if cursor == 0 { break } @@ -761,18 +770,17 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } else { cursor, _ = getEntryCursor() loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() - } - setEntryCursor(cursor) - - items = make([]types.SetEntryItem, len(loadedKey)) - for i, val := range loadedKey { - items[i].Value = val - if doConvert { - if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val { - items[i].DisplayValue = dv + items = make([]types.SetEntryItem, len(loadedKey)) + for i, val := range loadedKey { + items[i].Value = val + if doConvert { + if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val { + items[i].DisplayValue = dv + } } } } + setEntryCursor(cursor) return items, cursor == 0, nil } @@ -784,8 +792,8 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } case "zset": - loadZSetHandle := func() ([]types.ZSetItem, bool, error) { - var items []types.ZSetItem + loadZSetHandle := func() ([]types.ZSetEntryItem, bool, error) { + var items []types.ZSetEntryItem var cursor uint64 scanSize := int64(Preferences().GetScanSize()) var loadedVal []string @@ -800,10 +808,15 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS var score float64 for i := 0; i < len(loadedVal); i += 2 { if score, err = strconv.ParseFloat(loadedVal[i+1], 64); err == nil { - items = append(items, types.ZSetItem{ + items = append(items, types.ZSetEntryItem{ Value: loadedVal[i], Score: score, }) + if doConvert { + if dv, _, _ := strutil.ConvertTo(loadedVal[i], param.Decode, param.Format); dv != loadedVal[i] { + items[len(items)-1].DisplayValue = dv + } + } } } if cursor == 0 { @@ -813,13 +826,18 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } else { cursor, _ = getEntryCursor() loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() + loadedLen := len(loadedVal) + items = make([]types.ZSetEntryItem, loadedLen/2) var score float64 - for i := 0; i < len(loadedVal); i += 2 { + for i := 0; i < loadedLen; i += 2 { if score, err = strconv.ParseFloat(loadedVal[i+1], 64); err == nil { - items = append(items, types.ZSetItem{ - Value: loadedVal[i], - Score: score, - }) + items[i/2].Score = score + items[i/2].Value = loadedVal[i] + if doConvert { + if dv, _, _ := strutil.ConvertTo(loadedVal[i], param.Decode, param.Format); dv != loadedVal[i] { + items[i/2].DisplayValue = dv + } + } } } } @@ -835,9 +853,9 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } case "stream": - loadStreamHandle := func() ([]types.StreamItem, bool, error) { + loadStreamHandle := func() ([]types.StreamEntryItem, bool, error) { var msgs []redis.XMessage - var items []types.StreamItem + var items []types.StreamEntryItem var last string if param.Full { // load all @@ -865,7 +883,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } setEntryXLast(last) for _, msg := range msgs { - items = append(items, types.StreamItem{ + items = append(items, types.StreamEntryItem{ ID: msg.ID, Value: msg.Values, }) @@ -1227,8 +1245,8 @@ func (b *browserService) SetListItem(param types.SetListParam) (resp types.JSRes } // SetSetItem add members to set or remove from set -func (b *browserService) SetSetItem(connName string, db int, k any, remove bool, members []any) (resp types.JSResp) { - item, err := b.getRedisClient(connName, db) +func (b *browserService) SetSetItem(server string, db int, k any, remove bool, members []any) (resp types.JSResp) { + item, err := b.getRedisClient(server, db) if err != nil { resp.Msg = err.Error() return @@ -1289,45 +1307,54 @@ func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSRe } // UpdateZSetValue update value of sorted set member -func (b *browserService) UpdateZSetValue(connName string, db int, k any, value, newValue string, score float64) (resp types.JSResp) { - item, err := b.getRedisClient(connName, db) +func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.JSResp) { + item, err := b.getRedisClient(param.Server, param.DB) if err != nil { resp.Msg = err.Error() return } client, ctx := item.client, item.ctx - key := strutil.DecodeRedisKey(k) - updated := map[string]any{} + key := strutil.DecodeRedisKey(param.Key) + val, newVal := strutil.DecodeRedisKey(param.Value), strutil.DecodeRedisKey(param.NewValue) + updated := map[string]float64{} var removed []string - if len(newValue) <= 0 { - // blank new value, delete value - _, err = client.ZRem(ctx, key, value).Result() + if len(newVal) <= 0 { + // no new value, delete value + _, err = client.ZRem(ctx, key, val).Result() if err == nil { - removed = append(removed, value) - } - } else if newValue == value { - // update score only - _, err = client.ZAdd(ctx, key, redis.Z{ - Score: score, - Member: value, - }).Result() - if err == nil { - updated[value] = score + removed = append(removed, val) } } else { - // remove old value and add new one - _, err = client.ZRem(ctx, key, value).Result() - if err == nil { - removed = append(removed, value) + var saveVal string + if saveVal, err = strutil.SaveAs(newVal, param.Format, param.Decode); err != nil { + resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error()) + return } - _, err = client.ZAdd(ctx, key, redis.Z{ - Score: score, - Member: newValue, - }).Result() - if err == nil { - updated[newValue] = score + if saveVal == val { + // update score only + _, err = client.ZAdd(ctx, key, redis.Z{ + Score: param.Score, + Member: saveVal, + }).Result() + if err == nil { + updated[saveVal] = param.Score + } + } else { + // remove old value and add new one + _, err = client.ZRem(ctx, key, val).Result() + if err == nil { + removed = append(removed, val) + } + + _, err = client.ZAdd(ctx, key, redis.Z{ + Score: param.Score, + Member: saveVal, + }).Result() + if err == nil { + updated[saveVal] = param.Score + } } } if err != nil { diff --git a/backend/types/js_resp.go b/backend/types/js_resp.go index 89b5fa1..16e3598 100644 --- a/backend/types/js_resp.go +++ b/backend/types/js_resp.go @@ -79,3 +79,14 @@ type SetSetParam struct { Format string `json:"format,omitempty"` Decode string `json:"decode,omitempty"` } + +type SetZSetParam struct { + Server string `json:"server"` + DB int `json:"db"` + Key any `json:"key"` + Value any `json:"value"` + NewValue any `json:"newValue"` + Score float64 `json:"score"` + Format string `json:"format,omitempty"` + Decode string `json:"decode,omitempty"` +} diff --git a/backend/types/redis_wrapper.go b/backend/types/redis_wrapper.go index f830aab..1b9032d 100644 --- a/backend/types/redis_wrapper.go +++ b/backend/types/redis_wrapper.go @@ -16,12 +16,13 @@ type SetEntryItem struct { DisplayValue string `json:"dv,omitempty"` } -type ZSetItem struct { - Value string `json:"value"` - Score float64 `json:"score"` +type ZSetEntryItem struct { + Score float64 `json:"s"` + Value string `json:"v"` + DisplayValue string `json:"dv,omitempty"` } -type StreamItem struct { +type StreamEntryItem struct { ID string `json:"id"` Value map[string]any `json:"value"` } diff --git a/backend/utils/string/convert.go b/backend/utils/string/convert.go index 8b82e53..da70bb6 100644 --- a/backend/utils/string/convert.go +++ b/backend/utils/string/convert.go @@ -206,11 +206,11 @@ func autoViewAs(str string) (value, resultFormat string) { } func decodeJson(str string) (string, bool) { - str = strings.TrimSpace(str) - if (strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")) || - (strings.HasPrefix(str, "[") && strings.HasSuffix(str, "]")) { + trimedStr := strings.TrimSpace(str) + if (strings.HasPrefix(trimedStr, "{") && strings.HasSuffix(trimedStr, "}")) || + (strings.HasPrefix(trimedStr, "[") && strings.HasSuffix(trimedStr, "]")) { var out bytes.Buffer - if err := json.Indent(&out, []byte(str), "", " "); err == nil { + if err := json.Indent(&out, []byte(trimedStr), "", " "); err == nil { return out.String(), true } } @@ -282,9 +282,9 @@ func decodeBrotli(str string) (string, bool) { return str, false } -func SaveAs(str, viewType, decodeType string) (value string, err error) { +func SaveAs(str, format, decode string) (value string, err error) { value = str - switch viewType { + switch format { case types.FORMAT_JSON: if jsonStr, ok := encodeJson(str); ok { value = jsonStr @@ -310,7 +310,7 @@ func SaveAs(str, viewType, decodeType string) (value string, err error) { } } - switch decodeType { + switch decode { case types.DECODE_NONE: return @@ -350,18 +350,15 @@ func SaveAs(str, viewType, decodeType string) (value string, err error) { } return } - return str, errors.New("fail to save with unknown error") + return str, nil } func encodeJson(str string) (string, bool) { - var data any - if err := json.Unmarshal([]byte(str), &data); err == nil { - var jsonByte []byte - if jsonByte, err = json.Marshal(data); err == nil { - return string(jsonByte), true - } + var dst bytes.Buffer + if err := json.Compact(&dst, []byte(str)); err != nil { + return str, false } - return str, false + return dst.String(), true } func encodeBase64(str string) (string, bool) { diff --git a/frontend/src/components/content_value/ContentEntryEditor.vue b/frontend/src/components/content_value/ContentEntryEditor.vue index 442a6d7..a1e29d8 100644 --- a/frontend/src/components/content_value/ContentEntryEditor.vue +++ b/frontend/src/components/content_value/ContentEntryEditor.vue @@ -95,8 +95,8 @@ const pinBtnStyle = computed(() => ({ /** * - * @param {string} decode - * @param {string} format + * @param {decodeTypes} decode + * @param {formatTypes} format * @return {Promise} */ const onFormatChanged = async (decode = '', format = '') => { @@ -164,6 +164,7 @@ const onSave = () => { autofocus class="flex-item-expand" type="textarea" + :resizable="false" @update:value="onUpdateValue" /> { - currentEditRow.value = value currentEditRow.no = no currentEditRow.key = key + currentEditRow.value = value } const saveEdit = async (field, value, decode, format) => { @@ -194,13 +194,14 @@ const actionColumn = { onEdit: () => startEdit(index + 1, row.k, row.v), onDelete: async () => { try { - const { success, msg } = await browserStore.removeHashField( + const { removed, success, msg } = await browserStore.removeHashField( props.name, props.db, keyName.value, row.k, ) if (success) { + props.value.splice(index, 1) $message.success(i18n.t('dialogue.delete_key_succ', { key: row.k })) } else { $message.error(msg) diff --git a/frontend/src/components/content_value/ContentValueList.vue b/frontend/src/components/content_value/ContentValueList.vue index 0be9284..f705507 100644 --- a/frontend/src/components/content_value/ContentValueList.vue +++ b/frontend/src/components/content_value/ContentValueList.vue @@ -33,7 +33,11 @@ const props = defineProps({ type: Number, default: -1, }, - value: Object, + value: { + // [{v: string|number[], dv: string}] + type: Array, + default: () => {}, + }, size: Number, length: Number, format: { @@ -97,16 +101,16 @@ const valueColumn = reactive({ }) const startEdit = async (no, value) => { - currentEditRow.value = value currentEditRow.no = no + currentEditRow.value = value } /** * * @param {string|number} pos * @param {string} value - * @param {string} decode - * @param {string} format + * @param {decodeTypes} decode + * @param {formatTypes} format * @return {Promise} */ const saveEdit = async (pos, value, decode, format) => { @@ -171,6 +175,7 @@ const actionColumn = { index, ) if (success) { + props.value.splice(index, 1) $message.success(i18n.t('dialogue.delete_key_succ', { key: `#${index + 1}` })) } else { $message.error(msg) diff --git a/frontend/src/components/content_value/ContentValueSet.vue b/frontend/src/components/content_value/ContentValueSet.vue index 72c73f8..94588e5 100644 --- a/frontend/src/components/content_value/ContentValueSet.vue +++ b/frontend/src/components/content_value/ContentValueSet.vue @@ -33,7 +33,10 @@ const props = defineProps({ type: Number, default: -1, }, - value: Array, + value: { + type: Array, + default: () => [], + }, size: Number, length: Number, format: { @@ -97,16 +100,16 @@ const valueColumn = reactive({ }) const startEdit = async (no, value) => { - currentEditRow.value = value currentEditRow.no = no + currentEditRow.value = value } /** * * @param {string|number} pos * @param {string} value - * @param {string} decode - * @param {string} format + * @param {decodeTypes} decode + * @param {formatTypes} format * @return {Promise} */ const saveEdit = async (pos, value, decode, format) => { @@ -171,6 +174,7 @@ const actionColumn = { row.v, ) if (success) { + props.value.splice(index, 1) $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) } else { $message.error(msg) diff --git a/frontend/src/components/content_value/ContentValueWrapper.vue b/frontend/src/components/content_value/ContentValueWrapper.vue index 5a60519..cef5aa0 100644 --- a/frontend/src/components/content_value/ContentValueWrapper.vue +++ b/frontend/src/components/content_value/ContentValueWrapper.vue @@ -66,14 +66,15 @@ const keyName = computed(() => { const loadData = async (reset, full) => { try { - const { name, db, view, decodeType, matchPattern, decode, format } = data.value + if (!!props.blank) { + return + } + const { name, db, matchPattern, decode, format } = data.value reset = reset === true await browserStore.loadKeyDetail({ server: name, db: db, key: keyName.value, - viewType: view, - decodeType: decodeType, matchPattern: matchPattern, decode: reset ? decodeTypes.NONE : decode, format: reset ? formatTypes.RAW : format, diff --git a/frontend/src/components/content_value/ContentValueZSet.vue b/frontend/src/components/content_value/ContentValueZSet.vue index c62c117..cd846ee 100644 --- a/frontend/src/components/content_value/ContentValueZSet.vue +++ b/frontend/src/components/content_value/ContentValueZSet.vue @@ -3,10 +3,10 @@ import { computed, h, reactive, ref } from 'vue' import { useI18n } from 'vue-i18n' import ContentToolbar from './ContentToolbar.vue' import AddLink from '@/components/icons/AddLink.vue' -import { NButton, NCode, NIcon, NInput, NInputNumber, useThemeVars } from 'naive-ui' +import { NButton, NIcon, NInput, useThemeVars } from 'naive-ui' import { types, types as redisTypes } from '@/consts/support_redis_type.js' import EditableTableColumn from '@/components/common/EditableTableColumn.vue' -import { isEmpty, size } from 'lodash' +import { head, isEmpty, keys, size } from 'lodash' import useDialogStore from 'stores/dialog.js' import bytes from 'bytes' import { decodeTypes, formatTypes } from '@/consts/value_view_type.js' @@ -14,6 +14,9 @@ import useBrowserStore from 'stores/browser.js' import LoadList from '@/components/icons/LoadList.vue' import LoadAll from '@/components/icons/LoadAll.vue' import IconButton from '@/components/common/IconButton.vue' +import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue' +import FormatSelector from '@/components/content_value/FormatSelector.vue' +import Edit from '@/components/icons/Edit.vue' const i18n = useI18n() const themeVars = useThemeVars() @@ -30,12 +33,15 @@ const props = defineProps({ type: Number, default: -1, }, - value: Object, + value: { + type: Array, + default: () => [], + }, size: Number, length: Number, - viewAs: { + format: { type: String, - default: formatTypes.PLAIN_TEXT, + default: formatTypes.RAW, }, decode: { type: String, @@ -70,11 +76,22 @@ const filterType = ref(1) const browserStore = useBrowserStore() const dialogStore = useDialogStore() const keyType = redisTypes.ZSET -const currentEditRow = ref({ +const currentEditRow = reactive({ no: 0, score: 0, value: null, + format: formatTypes.RAW, + decode: decodeTypes.NONE, }) + +const inEdit = computed(() => { + return currentEditRow.no > 0 +}) +const fullEdit = ref(false) +const inFullEdit = computed(() => { + return inEdit.value && fullEdit.value +}) + const scoreColumn = reactive({ key: 'score', title: i18n.t('interface.score'), @@ -83,7 +100,7 @@ const scoreColumn = reactive({ resizable: true, filterOptionValue: null, filter(value, row) { - const score = parseFloat(row.score) + const score = parseFloat(row.s) if (isNaN(score)) { return true } @@ -110,23 +127,12 @@ const scoreColumn = reactive({ } } } else { - return !!~row.value.indexOf(value.toString()) + return !!~row.v.indexOf(value.toString()) } - return true }, render: (row) => { - const isEdit = currentEditRow.value.no === row.no - if (isEdit) { - return h(NInputNumber, { - value: currentEditRow.value.score, - 'onUpdate:value': (val) => { - currentEditRow.value.score = val - }, - }) - } else { - return row.score - } + return row.s }, }) const valueColumn = reactive({ @@ -139,33 +145,64 @@ const valueColumn = reactive({ tooltip: true, }, filterOptionValue: null, + className: inEdit ? 'clickable' : '', filter(value, row) { - return !!~row.value.indexOf(value.toString()) + return !!~row.v.indexOf(value.toString()) }, // sorter: (row1, row2) => row1.value - row2.value, - // ellipsis: { - // tooltip: true - // }, render: (row) => { - const isEdit = currentEditRow.value.no === row.no - if (isEdit) { - return h(NInput, { - value: currentEditRow.value.value, - type: 'textarea', - autosize: { minRow: 2, maxRows: 5 }, - style: 'text-align: left;', - 'onUpdate:value': (val) => { - currentEditRow.value.value = val - }, - }) - } else { - return h(NCode, { language: 'plaintext', wordWrap: true }, { default: () => row.value }) - } + return row.dv || row.v }, }) -const cancelEdit = () => { - currentEditRow.value.no = 0 +const startEdit = async (no, score, value) => { + currentEditRow.no = no + currentEditRow.score = score + currentEditRow.value = value +} + +const saveEdit = async (field, value, decode, format) => { + try { + const score = parseFloat(field) + const row = props.value[currentEditRow.no - 1] + if (row == null) { + throw new Error('row not exists') + } + + const { updated, success, msg } = await browserStore.updateZSetItem({ + server: props.name, + db: props.db, + key: keyName.value, + value: row.v, + newValue: value, + score, + decode, + format, + }) + if (success) { + row.v = head(keys(updated)) + row.s = updated[row.v] + const { value: displayVal } = await browserStore.convertValue({ + value: row.v, + decode: props.decode, + format: props.format, + }) + row.dv = displayVal + $message.success(i18n.t('dialogue.save_value_succ')) + } else { + $message.error(msg) + } + } catch (e) { + $message.error(e.message) + } +} + +const resetEdit = () => { + currentEditRow.no = 0 + currentEditRow.score = 0 + currentEditRow.value = null + currentEditRow.format = formatTypes.RAW + currentEditRow.decode = decodeTypes.NONE } const actionColumn = { @@ -175,25 +212,22 @@ const actionColumn = { align: 'center', titleAlign: 'center', fixed: 'right', - render: (row) => { + render: (row, index) => { return h(EditableTableColumn, { - editing: currentEditRow.value.no === row.no, - bindKey: row.value, - onEdit: () => { - currentEditRow.value.no = row.no - currentEditRow.value.value = row.value - currentEditRow.value.score = row.score - }, + editing: false, + bindKey: row.v, + onEdit: () => startEdit(index + 1, row.s, row.v), onDelete: async () => { try { const { success, msg } = await browserStore.removeZSetItem( props.name, props.db, keyName.value, - row.value, + row.v, ) if (success) { - $message.success(i18n.t('dialogue.delete_key_succ', { key: row.value })) + props.value.splice(index, 1) + $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) } else { $message.error(msg) } @@ -201,66 +235,51 @@ const actionColumn = { $message.error(e.message) } }, - onSave: async () => { - try { - const newValue = currentEditRow.value.value - if (isEmpty(newValue)) { - $message.error(i18n.t('dialogue.spec_field_required', { key: i18n.t('common.value') })) - return - } - const { success, msg } = await browserStore.updateZSetItem( - props.name, - props.db, - keyName.value, - row.value, - newValue, - currentEditRow.value.score, - ) - if (success) { - $message.success(i18n.t('dialogue.save_value_succ')) - } else { - $message.error(msg) - } - } catch (e) { - $message.error(e.message) - } finally { - currentEditRow.value.no = 0 - } - }, - onCancel: cancelEdit, }) }, } -const columns = reactive([ - { - key: 'no', - title: '#', - width: 80, - align: 'center', - titleAlign: 'center', - }, - valueColumn, - scoreColumn, - actionColumn, -]) -const tableData = computed(() => { - const data = [] - if (!isEmpty(props.value)) { - let index = 0 - for (const elem of props.value) { - data.push({ - no: ++index, - value: elem.value, - score: elem.score, - }) - } +const columns = computed(() => { + if (!inEdit.value) { + return [ + { + key: 'no', + title: '#', + width: 80, + align: 'center', + titleAlign: 'center', + render: (row, index) => { + return index + 1 + }, + }, + valueColumn, + scoreColumn, + actionColumn, + ] + } else { + return [ + { + key: 'no', + title: '#', + width: 80, + align: 'center', + titleAlign: 'center', + render: (row, index) => { + if (index + 1 === currentEditRow.no) { + // editing row, show edit state + return h(NIcon, { size: 16, color: 'red' }, () => h(Edit, { strokeWidth: 5 })) + } else { + return index + 1 + } + }, + }, + valueColumn, + ] } - return data }) const entries = computed(() => { - const len = size(tableData.value) + const len = size(props.value) return `${len} / ${Math.max(len, props.length)}` }) @@ -306,10 +325,14 @@ const onUpdateFilter = (filters, sourceColumn) => { } } +const onFormatChanged = (selDecode, selFormat) => { + emit('reload', selDecode, selFormat) +} + defineExpose({ reset: () => { clearFilter() - cancelEdit() + resetEdit() }, }) @@ -317,6 +340,7 @@ defineExpose({ diff --git a/frontend/src/components/dialogs/AddFieldsDialog.vue b/frontend/src/components/dialogs/AddFieldsDialog.vue index 656c0c7..f4e7383 100644 --- a/frontend/src/components/dialogs/AddFieldsDialog.vue +++ b/frontend/src/components/dialogs/AddFieldsDialog.vue @@ -95,9 +95,9 @@ const onAdd = async () => { { let data if (newForm.opType === 1) { - data = await browserStore.prependListItem(server, db, keyName, value) + data = await browserStore.prependListItem({ server, db, key: keyName, values: value }) } else { - data = await browserStore.appendListItem(server, db, keyName, value) + data = await browserStore.appendListItem({ server, db, key: keyName, values: value }) } const { success, msg } = data if (success) { diff --git a/frontend/src/main.js b/frontend/src/main.js index 848aa21..98c87ed 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -35,9 +35,9 @@ async function setupApp() { } catch (e) {} }) } - app.config.warnHandler = (message) => { - console.warn(message) - } + // app.config.warnHandler = (message) => { + // console.warn(message) + // } app.mount('#app') } diff --git a/frontend/src/stores/browser.js b/frontend/src/stores/browser.js index 3cebaa6..b0e4127 100644 --- a/frontend/src/stores/browser.js +++ b/frontend/src/stores/browser.js @@ -6,6 +6,7 @@ import { isEmpty, join, last, + map, remove, set, size, @@ -1082,21 +1083,21 @@ const useBrowserStore = defineStore('browser', { /** * remove hash field - * @param {string} connName + * @param {string} server * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string} field * @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>} */ - async removeHashField(connName, db, key, field) { + async removeHashField(server, db, key, field) { try { - const { data, success, msg } = await SetHashValue(connName, db, key, field, '', '') + const { data, success, msg } = await SetHashValue({ server, db, key, field, newField: '' }) if (success) { const { removed = [] } = data - if (!isEmpty(removed)) { - const tab = useTabStore() - tab.removeValueEntries({ server: connName, db, key, type: 'hash', entries: removed }) - } + // if (!isEmpty(removed)) { + // const tab = useTabStore() + // tab.removeValueEntries({ server, db, key, type: 'hash', entries: removed }) + // } return { success, removed } } else { return { success, msg } @@ -1125,25 +1126,27 @@ const useBrowserStore = defineStore('browser', { /** * prepend item to head of list - * @param connName - * @param db - * @param key - * @param values + * @param {string} server + * @param {number} db + * @param {string|number[]} key + * @param {string[]} values * @returns {Promise<{[msg]: string, success: boolean, [item]: []}>} */ - async prependListItem(connName, db, key, values) { + async prependListItem({ server, db, key, values }) { try { - const { data, success, msg } = await AddListItem(connName, db, key, 0, values) + const { data, success, msg } = await AddListItem(server, db, key, 0, values) if (success) { const { left = [] } = data if (!isEmpty(left)) { + // TODO: convert to display value + const entries = map(left, (v) => ({ v })) const tab = useTabStore() tab.upsertValueEntries({ - server: connName, + server: server, db, key, type: 'list', - entries: right, + entries, prepend: true, }) } @@ -1158,25 +1161,26 @@ const useBrowserStore = defineStore('browser', { /** * append item to tail of list - * @param connName - * @param db - * @param key - * @param values + * @param {string} server + * @param {number} db + * @param {string|number[]} key + * @param {string[]} values * @returns {Promise<{[msg]: string, success: boolean, [item]: any[]}>} */ - async appendListItem(connName, db, key, values) { + async appendListItem({ server, db, key, values }) { try { - const { data, success, msg } = await AddListItem(connName, db, key, 1, values) + const { data, success, msg } = await AddListItem(server, db, key, 1, values) if (success) { const { right = [] } = data if (!isEmpty(right)) { + const entries = map(right, (v) => ({ v })) const tab = useTabStore() tab.upsertValueEntries({ - server: connName, + server: server, db, key, type: 'list', - entries: right, + entries, prepend: false, }) } @@ -1196,8 +1200,8 @@ const useBrowserStore = defineStore('browser', { * @param {string|number[]} key * @param {number} index * @param {string|number[]} value - * @param {string} decode - * @param {string} format + * @param {decodeTypes} decode + * @param {formatTypes} format * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} */ async updateListItem({ server, db, key, index, value, decode = decodeTypes.NONE, format = formatTypes.RAW }) { @@ -1237,16 +1241,16 @@ const useBrowserStore = defineStore('browser', { const { data, success, msg } = await SetListItem({ server, db, key, index }) if (success) { const { removed = [] } = data - if (!isEmpty(removed)) { - const tab = useTabStore() - tab.removeValueEntries({ - server, - db, - key, - type: 'list', - entries: removed, - }) - } + // if (!isEmpty(removed)) { + // const tab = useTabStore() + // tab.removeValueEntries({ + // server, + // db, + // key, + // type: 'list', + // entries: removed, + // }) + // } return { success, removed } } else { return { success, msg } @@ -1311,18 +1315,18 @@ const useBrowserStore = defineStore('browser', { /** * remove item from set - * @param {string} connName + * @param {string} server * @param {number} db * @param {string|number[]} key * @param {string} value * @returns {Promise<{[msg]: string, success: boolean}>} */ - async removeSetItem(connName, db, key, value) { + async removeSetItem(server, db, key, value) { try { - const { success, msg } = await SetSetItem(connName, db, key, true, [value]) + const { success, msg } = await SetSetItem(server, db, key, true, [value]) if (success) { - const tab = useTabStore() - tab.removeValueEntries({ server: connName, db, key, type: 'set', entries: [value] }) + // const tab = useTabStore() + // tab.removeValueEntries({ server: connName, db, key, type: 'set', entries: [value] }) return { success } } else { return { success, msg } @@ -1358,26 +1362,46 @@ const useBrowserStore = defineStore('browser', { /** * update item of sorted set - * @param {string} connName + * @param {string} server * @param {number} db * @param {string|number[]} key * @param {string} value * @param {string} newValue * @param {number} score + * @param {decodeTypes} decode + * @param {formatTypes} format * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}, [removed]: []}>} */ - async updateZSetItem(connName, db, key, value, newValue, score) { + async updateZSetItem({ + server, + db, + key, + value = '', + newValue, + score, + decode = decodeTypes.NONE, + format = formatTypes.RAW, + }) { try { - const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, newValue, score) + const { data, success, msg } = await UpdateZSetValue({ + server, + db, + key, + value, + newValue, + score, + decode, + format, + }) if (success) { const { updated, removed } = data - const tab = useTabStore() - if (!isEmpty(updated)) { - tab.upsertValueEntries({ server: connName, db, key, type: 'zset', entries: updated }) - } - if (!isEmpty(removed)) { - tab.removeValueEntries({ server: connName, db, key, type: 'zset', entries: removed }) - } + // const tab = useTabStore() + // if (!isEmpty(updated)) { + // tab.upsertValueEntries({ server, db, key, type: 'zset', entries: updated }) + // } + // if (!isEmpty(removed)) { + // tab.removeValueEntries({ server, db, key, type: 'zset', entries: removed }) + // } return { success, updated, removed } } else { return { success, msg } @@ -1389,22 +1413,22 @@ const useBrowserStore = defineStore('browser', { /** * remove item from sorted set - * @param {string} connName + * @param {string} server * @param {number} db * @param {string|number[]} key * @param {string} value * @returns {Promise<{[msg]: string, success: boolean, [removed]: []}>} */ - async removeZSetItem(connName, db, key, value) { + async removeZSetItem(server, db, key, value) { try { - const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, '', 0) + const { data, success, msg } = await UpdateZSetValue({ server, db, key, value, newValue: '', score: 0 }) if (success) { const { removed } = data - if (!isEmpty(removed)) { - const tab = useTabStore() - tab.removeValueEntries({ server: connName, db, key, type: 'zset', entries: removed }) - } - return { success } + // if (!isEmpty(removed)) { + // const tab = useTabStore() + // tab.removeValueEntries({ server: server, db, key, type: 'zset', entries: removed }) + // } + return { success, removed } } else { return { success, msg } }