mirror of
				https://github.com/tiny-craft/tiny-rdm.git
				synced 2025-11-01 02:52:33 +08:00 
			
		
		
		
	perf: set value support format viewing #65
This commit is contained in:
		| @@ -580,6 +580,11 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 		resp.Msg = "key not exists" | 		resp.Msg = "key not exists" | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	var doConvert bool | ||||||
|  | 	if (len(param.Decode) > 0 && param.Decode != types.DECODE_NONE) || | ||||||
|  | 		(len(param.Format) > 0 && param.Format != types.FORMAT_RAW) { | ||||||
|  | 		doConvert = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var data types.KeyDetail | 	var data types.KeyDetail | ||||||
| 	//var cursor uint64 | 	//var cursor uint64 | ||||||
| @@ -636,14 +641,15 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 		loadListHandle := func() ([]types.ListEntryItem, bool, error) { | 		loadListHandle := func() ([]types.ListEntryItem, bool, error) { | ||||||
| 			var loadVal []string | 			var loadVal []string | ||||||
| 			var cursor uint64 | 			var cursor uint64 | ||||||
|  | 			var subErr error | ||||||
| 			if param.Full { | 			if param.Full { | ||||||
| 				// load all | 				// load all | ||||||
| 				cursor = 0 | 				cursor = 0 | ||||||
| 				loadVal, err = client.LRange(ctx, key, 0, -1).Result() | 				loadVal, subErr = client.LRange(ctx, key, 0, -1).Result() | ||||||
| 			} else { | 			} else { | ||||||
| 				cursor, _ = getEntryCursor() | 				cursor, _ = getEntryCursor() | ||||||
| 				scanSize := int64(Preferences().GetScanSize()) | 				scanSize := int64(Preferences().GetScanSize()) | ||||||
| 				loadVal, err = client.LRange(ctx, key, int64(cursor), int64(cursor)+scanSize-1).Result() | 				loadVal, subErr = client.LRange(ctx, key, int64(cursor), int64(cursor)+scanSize-1).Result() | ||||||
| 				cursor = cursor + uint64(scanSize) | 				cursor = cursor + uint64(scanSize) | ||||||
| 				if len(loadVal) < int(scanSize) { | 				if len(loadVal) < int(scanSize) { | ||||||
| 					cursor = 0 | 					cursor = 0 | ||||||
| @@ -652,12 +658,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 			setEntryCursor(cursor) | 			setEntryCursor(cursor) | ||||||
|  |  | ||||||
| 			items := make([]types.ListEntryItem, len(loadVal)) | 			items := make([]types.ListEntryItem, len(loadVal)) | ||||||
| 			doConvert := len(param.Decode) > 0 && len(param.Format) > 0 |  | ||||||
| 			for i, val := range loadVal { | 			for i, val := range loadVal { | ||||||
| 				items[i] = types.ListEntryItem{ |  | ||||||
| 					Value:        val, |  | ||||||
| 					DisplayValue: "", |  | ||||||
| 				} |  | ||||||
| 				items[i].Value = val | 				items[i].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 { | ||||||
| @@ -665,8 +666,8 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			if err != nil { | 			if subErr != nil { | ||||||
| 				return items, false, err | 				return items, false, subErr | ||||||
| 			} | 			} | ||||||
| 			return items, cursor == 0, nil | 			return items, cursor == 0, nil | ||||||
| 		} | 		} | ||||||
| @@ -685,14 +686,14 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 			items := make([]types.HashEntryItem, 0, scanSize) | 			items := make([]types.HashEntryItem, 0, scanSize) | ||||||
| 			var loadedVal []string | 			var loadedVal []string | ||||||
| 			var cursor uint64 | 			var cursor uint64 | ||||||
| 			doConvert := len(param.Decode) > 0 && len(param.Format) > 0 | 			var subErr error | ||||||
| 			if param.Full { | 			if param.Full { | ||||||
| 				// load all | 				// load all | ||||||
| 				cursor = 0 | 				cursor = 0 | ||||||
| 				for { | 				for { | ||||||
| 					loadedVal, cursor, err = client.HScan(ctx, key, cursor, "*", scanSize).Result() | 					loadedVal, cursor, subErr = client.HScan(ctx, key, cursor, "*", scanSize).Result() | ||||||
| 					if err != nil { | 					if subErr != nil { | ||||||
| 						return nil, false, err | 						return nil, 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{ | ||||||
| @@ -711,9 +712,9 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				cursor, _ = getEntryCursor() | 				cursor, _ = getEntryCursor() | ||||||
| 				loadedVal, cursor, err = client.HScan(ctx, key, cursor, matchPattern, scanSize).Result() | 				loadedVal, cursor, subErr = client.HScan(ctx, key, cursor, matchPattern, scanSize).Result() | ||||||
| 				if err != nil { | 				if subErr != nil { | ||||||
| 					return nil, false, err | 					return nil, 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{ | ||||||
| @@ -739,30 +740,39 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case "set": | 	case "set": | ||||||
| 		loadSetHandle := func() ([]string, bool, error) { | 		loadSetHandle := func() ([]types.SetEntryItem, bool, error) { | ||||||
| 			var items []string | 			var items []types.SetEntryItem | ||||||
| 			var cursor uint64 | 			var cursor uint64 | ||||||
|  | 			var subErr error | ||||||
| 			scanSize := int64(Preferences().GetScanSize()) | 			scanSize := int64(Preferences().GetScanSize()) | ||||||
| 			var loadedKey []string | 			var loadedKey []string | ||||||
| 			if param.Full { | 			if param.Full { | ||||||
| 				// load all | 				// load all | ||||||
| 				cursor = 0 | 				cursor = 0 | ||||||
| 				for { | 				for { | ||||||
| 					loadedKey, cursor, err = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() | 					loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() | ||||||
| 					if err != nil { | 					if subErr != nil { | ||||||
| 						return items, false, err | 						return items, false, subErr | ||||||
| 					} | 					} | ||||||
| 					items = append(items, loadedKey...) |  | ||||||
| 					if cursor == 0 { | 					if cursor == 0 { | ||||||
| 						break | 						break | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				cursor, _ = getEntryCursor() | 				cursor, _ = getEntryCursor() | ||||||
| 				loadedKey, cursor, err = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() | 				loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() | ||||||
| 				items = append(items, loadedKey...) |  | ||||||
| 			} | 			} | ||||||
| 			setEntryCursor(cursor) | 			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 | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			return items, cursor == 0, nil | 			return items, cursor == 0, nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -1245,23 +1255,36 @@ func (b *browserService) SetSetItem(connName string, db int, k any, remove bool, | |||||||
| } | } | ||||||
|  |  | ||||||
| // UpdateSetItem replace member of set | // UpdateSetItem replace member of set | ||||||
| func (b *browserService) UpdateSetItem(connName string, db int, k any, value, newValue string) (resp types.JSResp) { | func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSResp) { | ||||||
| 	item, err := b.getRedisClient(connName, db) | 	item, err := b.getRedisClient(param.Server, param.DB) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		resp.Msg = err.Error() | 		resp.Msg = err.Error() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	client, ctx := item.client, item.ctx | 	client, ctx := item.client, item.ctx | ||||||
| 	key := strutil.DecodeRedisKey(k) | 	key := strutil.DecodeRedisKey(param.Key) | ||||||
| 	_, _ = client.SRem(ctx, key, value).Result() | 	// remove old value | ||||||
| 	_, err = client.SAdd(ctx, key, newValue).Result() | 	str := strutil.DecodeRedisKey(param.Value) | ||||||
|  | 	_, _ = client.SRem(ctx, key, str).Result() | ||||||
|  |  | ||||||
|  | 	// insert new value | ||||||
|  | 	str = strutil.DecodeRedisKey(param.NewValue) | ||||||
|  | 	var saveStr string | ||||||
|  | 	if saveStr, err = strutil.SaveAs(str, param.Format, param.Decode); err != nil { | ||||||
|  | 		resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	_, err = client.SAdd(ctx, key, saveStr).Result() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		resp.Msg = err.Error() | 		resp.Msg = err.Error() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	resp.Success = true | 	resp.Success = true | ||||||
|  | 	resp.Data = map[string]any{ | ||||||
|  | 		"added": saveStr, | ||||||
|  | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -30,17 +30,6 @@ type KeyDetailParam struct { | |||||||
| 	Full         bool   `json:"full"` | 	Full         bool   `json:"full"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type ListEntryItem struct { |  | ||||||
| 	Value        any    `json:"v"` |  | ||||||
| 	DisplayValue string `json:"dv,omitempty"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type HashEntryItem struct { |  | ||||||
| 	Key          string `json:"k"` |  | ||||||
| 	Value        any    `json:"v"` |  | ||||||
| 	DisplayValue string `json:"dv,omitempty"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type KeyDetail struct { | type KeyDetail struct { | ||||||
| 	Value  any    `json:"value"` | 	Value  any    `json:"value"` | ||||||
| 	Length int64  `json:"length,omitempty"` | 	Length int64  `json:"length,omitempty"` | ||||||
| @@ -80,3 +69,13 @@ type SetHashParam struct { | |||||||
| 	Format   string `json:"format,omitempty"` | 	Format   string `json:"format,omitempty"` | ||||||
| 	Decode   string `json:"decode,omitempty"` | 	Decode   string `json:"decode,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type SetSetParam struct { | ||||||
|  | 	Server   string `json:"server"` | ||||||
|  | 	DB       int    `json:"db"` | ||||||
|  | 	Key      any    `json:"key"` | ||||||
|  | 	Value    any    `json:"value"` | ||||||
|  | 	NewValue any    `json:"newValue"` | ||||||
|  | 	Format   string `json:"format,omitempty"` | ||||||
|  | 	Decode   string `json:"decode,omitempty"` | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,5 +1,21 @@ | |||||||
| package types | package types | ||||||
|  |  | ||||||
|  | type ListEntryItem struct { | ||||||
|  | 	Value        any    `json:"v"` | ||||||
|  | 	DisplayValue string `json:"dv,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type HashEntryItem struct { | ||||||
|  | 	Key          string `json:"k"` | ||||||
|  | 	Value        any    `json:"v"` | ||||||
|  | 	DisplayValue string `json:"dv,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SetEntryItem struct { | ||||||
|  | 	Value        any    `json:"v"` | ||||||
|  | 	DisplayValue string `json:"dv,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
| type ZSetItem struct { | type ZSetItem struct { | ||||||
| 	Value string  `json:"value"` | 	Value string  `json:"value"` | ||||||
| 	Score float64 `json:"score"` | 	Score float64 `json:"score"` | ||||||
|   | |||||||
| @@ -106,6 +106,8 @@ func decodeWith(str, decodeType string) (value, resultDecode string) { | |||||||
| // if no decode is possible, it will return the origin string value and "none" decode type | // if no decode is possible, it will return the origin string value and "none" decode type | ||||||
| func autoDecode(str string) (value, resultDecode string) { | func autoDecode(str string) (value, resultDecode string) { | ||||||
| 	if len(str) > 0 { | 	if len(str) > 0 { | ||||||
|  | 		// pure digit content may incorrect regard as some encoded type, skip decode | ||||||
|  | 		if match, _ := regexp.MatchString(`^\d+$`, str); !match { | ||||||
| 			var ok bool | 			var ok bool | ||||||
| 			if value, ok = decodeBase64(str); ok { | 			if value, ok = decodeBase64(str); ok { | ||||||
| 				resultDecode = types.DECODE_BASE64 | 				resultDecode = types.DECODE_BASE64 | ||||||
| @@ -133,6 +135,7 @@ func autoDecode(str string) (value, resultDecode string) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	value = str | 	value = str | ||||||
| 	resultDecode = types.DECODE_NONE | 	resultDecode = types.DECODE_NONE | ||||||
| @@ -215,13 +218,11 @@ func decodeJson(str string) (string, bool) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func decodeBase64(str string) (string, bool) { | func decodeBase64(str string) (string, bool) { | ||||||
| 	if match, _ := regexp.MatchString(`^\d+$`, str); !match { |  | ||||||
| 	if decodedStr, err := base64.StdEncoding.DecodeString(str); err == nil { | 	if decodedStr, err := base64.StdEncoding.DecodeString(str); err == nil { | ||||||
| 		if s := string(decodedStr); !containsBinary(s) { | 		if s := string(decodedStr); !containsBinary(s) { | ||||||
| 			return s, true | 			return s, true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| 	return str, false | 	return str, false | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -373,7 +373,6 @@ defineExpose({ | |||||||
|         <div id="content-table" class="value-wrapper value-item-part flex-box-h flex-item-expand"> |         <div id="content-table" class="value-wrapper value-item-part flex-box-h flex-item-expand"> | ||||||
|             <!-- table --> |             <!-- table --> | ||||||
|             <n-data-table |             <n-data-table | ||||||
|                 :key="(row) => row.no" |  | ||||||
|                 ref="tableRef" |                 ref="tableRef" | ||||||
|                 :bordered="false" |                 :bordered="false" | ||||||
|                 :bottom-bordered="false" |                 :bottom-bordered="false" | ||||||
|   | |||||||
| @@ -84,7 +84,6 @@ const valueColumn = reactive({ | |||||||
|     }, |     }, | ||||||
|     render: (row) => { |     render: (row) => { | ||||||
|         // if (!isEmpty(row.dv)) { |         // if (!isEmpty(row.dv)) { | ||||||
|         //     console.log(row.dv) |  | ||||||
|         //     return h(NCode, { language: 'json', wordWrap: true, code: row.dv }) |         //     return h(NCode, { language: 'json', wordWrap: true, code: row.dv }) | ||||||
|         // } |         // } | ||||||
|         return row.dv || row.v |         return row.dv || row.v | ||||||
| @@ -96,6 +95,14 @@ const startEdit = async (no, value) => { | |||||||
|     currentEditRow.no = no |     currentEditRow.no = no | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @param {string|number} pos | ||||||
|  |  * @param {string} value | ||||||
|  |  * @param {string} decode | ||||||
|  |  * @param {string} format | ||||||
|  |  * @return {Promise<void>} | ||||||
|  |  */ | ||||||
| const saveEdit = async (pos, value, decode, format) => { | const saveEdit = async (pos, value, decode, format) => { | ||||||
|     try { |     try { | ||||||
|         const index = parseInt(pos) - 1 |         const index = parseInt(pos) - 1 | ||||||
| @@ -149,8 +156,6 @@ const actionColumn = { | |||||||
|             editing: false, |             editing: false, | ||||||
|             bindKey: `#${index + 1}`, |             bindKey: `#${index + 1}`, | ||||||
|             onEdit: () => { |             onEdit: () => { | ||||||
|                 currentEditRow.no = index + 1 |  | ||||||
|                 currentEditRow.value = row |  | ||||||
|                 startEdit(index + 1, row.v) |                 startEdit(index + 1, row.v) | ||||||
|             }, |             }, | ||||||
|             onDelete: async () => { |             onDelete: async () => { | ||||||
| @@ -162,7 +167,7 @@ const actionColumn = { | |||||||
|                         index, |                         index, | ||||||
|                     ) |                     ) | ||||||
|                     if (success) { |                     if (success) { | ||||||
|                         $message.success(i18n.t('dialogue.delete_key_succ', { key: '#' + row.no })) |                         $message.success(i18n.t('dialogue.delete_key_succ', { key: `#${index + 1}` })) | ||||||
|                     } else { |                     } else { | ||||||
|                         $message.error(msg) |                         $message.error(msg) | ||||||
|                     } |                     } | ||||||
| @@ -307,7 +312,6 @@ defineExpose({ | |||||||
|         <div class="value-wrapper value-item-part flex-box-h flex-item-expand"> |         <div class="value-wrapper value-item-part flex-box-h flex-item-expand"> | ||||||
|             <!-- table --> |             <!-- table --> | ||||||
|             <n-data-table |             <n-data-table | ||||||
|                 :key="(row) => row.no" |  | ||||||
|                 :bordered="false" |                 :bordered="false" | ||||||
|                 :bottom-bordered="false" |                 :bottom-bordered="false" | ||||||
|                 :columns="columns" |                 :columns="columns" | ||||||
|   | |||||||
| @@ -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, NIcon, NInput, 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' | ||||||
| @@ -14,6 +14,9 @@ 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 Edit from '@/components/icons/Edit.vue' | ||||||
|  | import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue' | ||||||
|  | import FormatSelector from '@/components/content_value/FormatSelector.vue' | ||||||
|  |  | ||||||
| const i18n = useI18n() | const i18n = useI18n() | ||||||
| const themeVars = useThemeVars() | const themeVars = useThemeVars() | ||||||
| @@ -33,7 +36,7 @@ const props = defineProps({ | |||||||
|     value: Array, |     value: Array, | ||||||
|     size: Number, |     size: Number, | ||||||
|     length: Number, |     length: Number, | ||||||
|     viewAs: { |     format: { | ||||||
|         type: String, |         type: String, | ||||||
|         default: formatTypes.RAW, |         default: formatTypes.RAW, | ||||||
|     }, |     }, | ||||||
| @@ -58,40 +61,87 @@ const keyName = computed(() => { | |||||||
| const browserStore = useBrowserStore() | const browserStore = useBrowserStore() | ||||||
| const dialogStore = useDialogStore() | const dialogStore = useDialogStore() | ||||||
| const keyType = redisTypes.SET | const keyType = redisTypes.SET | ||||||
| const currentEditRow = ref({ | const currentEditRow = reactive({ | ||||||
|     no: 0, |     no: 0, | ||||||
|     value: null, |     value: null, | ||||||
|  |     format: formatTypes.RAW, | ||||||
|  |     decode: decodeTypes.NONE, | ||||||
|  | }) | ||||||
|  | const inEdit = computed(() => { | ||||||
|  |     return currentEditRow.no > 0 | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const valueColumn = reactive({ | const valueColumn = reactive({ | ||||||
|     key: 'value', |     key: 'value', | ||||||
|     title: i18n.t('common.value'), |     title: i18n.t('common.value'), | ||||||
|     align: 'center', |     align: 'center', | ||||||
|     titleAlign: 'center', |     titleAlign: 'center', | ||||||
|  |     ellipsis: { | ||||||
|  |         tooltip: true, | ||||||
|  |     }, | ||||||
|     filterOptionValue: null, |     filterOptionValue: null, | ||||||
|     filter(value, row) { |     filter: (value, row) => { | ||||||
|         return !!~row.value.indexOf(value.toString()) |         return !!~row.v.indexOf(value.toString()) | ||||||
|     }, |     }, | ||||||
|     render: (row) => { |     render: (row) => { | ||||||
|         const isEdit = currentEditRow.value.no === row.no |         // if (!isEmpty(row.dv)) { | ||||||
|         if (isEdit) { |         //     return h(NCode, { language: 'json', wordWrap: true, code: row.dv }) | ||||||
|             return h(NInput, { |         // } | ||||||
|                 value: currentEditRow.value.value, |         return row.dv || row.v | ||||||
|                 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 }) |  | ||||||
|         } |  | ||||||
|     }, |     }, | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const cancelEdit = () => { | const startEdit = async (no, value) => { | ||||||
|     currentEditRow.value.no = 0 |     currentEditRow.value = value | ||||||
|  |     currentEditRow.no = no | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @param {string|number} pos | ||||||
|  |  * @param {string} value | ||||||
|  |  * @param {string} decode | ||||||
|  |  * @param {string} format | ||||||
|  |  * @return {Promise<void>} | ||||||
|  |  */ | ||||||
|  | const saveEdit = async (pos, value, decode, format) => { | ||||||
|  |     try { | ||||||
|  |         const index = parseInt(pos) - 1 | ||||||
|  |         const row = props.value[index] | ||||||
|  |         if (row == null) { | ||||||
|  |             throw new Error('row not exists') | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const { added, success, msg } = await browserStore.updateSetItem({ | ||||||
|  |             server: props.name, | ||||||
|  |             db: props.db, | ||||||
|  |             key: keyName.value, | ||||||
|  |             value: row.v, | ||||||
|  |             newValue: value, | ||||||
|  |             decode, | ||||||
|  |             format, | ||||||
|  |         }) | ||||||
|  |         if (success) { | ||||||
|  |             row.v = added | ||||||
|  |             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) | ||||||
|  |     } finally { | ||||||
|  |         resetEdit() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const resetEdit = () => { | ||||||
|  |     currentEditRow.no = 0 | ||||||
|  |     currentEditRow.value = null | ||||||
| } | } | ||||||
|  |  | ||||||
| const actionColumn = { | const actionColumn = { | ||||||
| @@ -101,14 +151,12 @@ const actionColumn = { | |||||||
|     align: 'center', |     align: 'center', | ||||||
|     titleAlign: 'center', |     titleAlign: 'center', | ||||||
|     fixed: 'right', |     fixed: 'right', | ||||||
|     render: (row) => { |     render: (row, index) => { | ||||||
|         return h(EditableTableColumn, { |         return h(EditableTableColumn, { | ||||||
|             editing: currentEditRow.value.no === row.no, |             editing: false, | ||||||
|             bindKey: row.value, |             bindKey: `#${index + 1}`, | ||||||
|             onEdit: () => { |             onEdit: () => { | ||||||
|                 currentEditRow.value.no = row.no |                 startEdit(index + 1, row.v) | ||||||
|                 currentEditRow.value.key = row.key |  | ||||||
|                 currentEditRow.value.value = row.value |  | ||||||
|             }, |             }, | ||||||
|             onDelete: async () => { |             onDelete: async () => { | ||||||
|                 try { |                 try { | ||||||
| @@ -116,10 +164,10 @@ const actionColumn = { | |||||||
|                         props.name, |                         props.name, | ||||||
|                         props.db, |                         props.db, | ||||||
|                         keyName.value, |                         keyName.value, | ||||||
|                         row.value, |                         row.v, | ||||||
|                     ) |                     ) | ||||||
|                     if (success) { |                     if (success) { | ||||||
|                         $message.success(i18n.t('dialogue.delete_key_succ', { key: row.value })) |                         $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) | ||||||
|                     } else { |                     } else { | ||||||
|                         $message.error(msg) |                         $message.error(msg) | ||||||
|                     } |                     } | ||||||
| @@ -127,31 +175,12 @@ const actionColumn = { | |||||||
|                     $message.error(e.message) |                     $message.error(e.message) | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             onSave: async () => { |  | ||||||
|                 try { |  | ||||||
|                     const { success, msg } = await browserStore.updateSetItem( |  | ||||||
|                         props.name, |  | ||||||
|                         props.db, |  | ||||||
|                         keyName.value, |  | ||||||
|                         row.value, |  | ||||||
|                         currentEditRow.value.value, |  | ||||||
|                     ) |  | ||||||
|                     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 = computed(() => { | const columns = computed(() => { | ||||||
|  |     if (!inEdit.value) { | ||||||
|         return [ |         return [ | ||||||
|             { |             { | ||||||
|                 key: 'no', |                 key: 'no', | ||||||
| @@ -159,26 +188,48 @@ const columns = computed(() => { | |||||||
|                 width: 80, |                 width: 80, | ||||||
|                 align: 'center', |                 align: 'center', | ||||||
|                 titleAlign: 'center', |                 titleAlign: 'center', | ||||||
|  |                 render: (row, index) => { | ||||||
|  |                     return index + 1 | ||||||
|  |                 }, | ||||||
|             }, |             }, | ||||||
|             valueColumn, |             valueColumn, | ||||||
|             actionColumn, |             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, | ||||||
|  |         ] | ||||||
|  |     } | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const tableData = computed(() => { | const rowProps = (row, index) => { | ||||||
|     const data = [] |     return { | ||||||
|     const len = size(props.value) |         onClick: () => { | ||||||
|     for (let i = 0; i < len; i++) { |             // in edit mode, switch edit row by click | ||||||
|         data.push({ |             if (inEdit.value) { | ||||||
|             no: i + 1, |                 startEdit(index + 1, row.v) | ||||||
|             value: props.value[i], |  | ||||||
|         }) |  | ||||||
|             } |             } | ||||||
|     return data |         }, | ||||||
| }) |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| const entries = computed(() => { | const entries = computed(() => { | ||||||
|     const len = size(tableData.value) |     const len = size(props.value) | ||||||
|     return `${len} / ${Math.max(len, props.length)}` |     return `${len} / ${Math.max(len, props.length)}` | ||||||
| }) | }) | ||||||
|  |  | ||||||
| @@ -199,10 +250,14 @@ const onUpdateFilter = (filters, sourceColumn) => { | |||||||
|     valueColumn.filterOptionValue = filters[sourceColumn.key] |     valueColumn.filterOptionValue = filters[sourceColumn.key] | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const onFormatChanged = (selDecode, selFormat) => { | ||||||
|  |     emit('reload', selDecode, selFormat) | ||||||
|  | } | ||||||
|  |  | ||||||
| defineExpose({ | defineExpose({ | ||||||
|     reset: () => { |     reset: () => { | ||||||
|         clearFilter() |         clearFilter() | ||||||
|         cancelEdit() |         resetEdit() | ||||||
|     }, |     }, | ||||||
| }) | }) | ||||||
| </script> | </script> | ||||||
| @@ -254,14 +309,15 @@ defineExpose({ | |||||||
|                 {{ $t('interface.add_row') }} |                 {{ $t('interface.add_row') }} | ||||||
|             </n-button> |             </n-button> | ||||||
|         </div> |         </div> | ||||||
|         <div class="value-wrapper value-item-part flex-box-v flex-item-expand"> |         <div class="value-wrapper value-item-part flex-box-h flex-item-expand"> | ||||||
|  |             <!-- table --> | ||||||
|             <n-data-table |             <n-data-table | ||||||
|                 :key="(row) => row.no" |  | ||||||
|                 :bordered="false" |                 :bordered="false" | ||||||
|                 :bottom-bordered="false" |                 :bottom-bordered="false" | ||||||
|                 :columns="columns" |                 :columns="columns" | ||||||
|                 :data="tableData" |                 :data="props.value" | ||||||
|                 :loading="props.loading" |                 :loading="props.loading" | ||||||
|  |                 :row-props="rowProps" | ||||||
|                 :single-column="true" |                 :single-column="true" | ||||||
|                 :single-line="false" |                 :single-line="false" | ||||||
|                 class="flex-item-expand" |                 class="flex-item-expand" | ||||||
| @@ -270,12 +326,33 @@ defineExpose({ | |||||||
|                 striped |                 striped | ||||||
|                 virtual-scroll |                 virtual-scroll | ||||||
|                 @update:filters="onUpdateFilter" /> |                 @update:filters="onUpdateFilter" /> | ||||||
|  |  | ||||||
|  |             <!-- edit pane --> | ||||||
|  |             <content-entry-editor | ||||||
|  |                 v-show="inEdit" | ||||||
|  |                 :decode="currentEditRow.decode" | ||||||
|  |                 :field="currentEditRow.no" | ||||||
|  |                 :field-label="$t('common.index')" | ||||||
|  |                 :field-readonly="true" | ||||||
|  |                 :format="currentEditRow.format" | ||||||
|  |                 :value="currentEditRow.value" | ||||||
|  |                 :value-label="$t('common.value')" | ||||||
|  |                 class="flex-item-expand" | ||||||
|  |                 style="width: 100%" | ||||||
|  |                 @cancel="resetEdit" | ||||||
|  |                 @save="saveEdit" /> | ||||||
|         </div> |         </div> | ||||||
|         <div class="value-footer flex-box-h"> |         <div class="value-footer flex-box-h"> | ||||||
|             <n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text> |             <n-text v-if="!isNaN(props.length)">{{ $t('interface.entries') }}: {{ entries }}</n-text> | ||||||
|             <n-divider v-if="!isNaN(props.length)" vertical /> |             <n-divider v-if="!isNaN(props.length)" vertical /> | ||||||
|             <n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text> |             <n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text> | ||||||
|             <div class="flex-item-expand"></div> |             <div class="flex-item-expand"></div> | ||||||
|  |             <format-selector | ||||||
|  |                 v-show="!inEdit" | ||||||
|  |                 :decode="props.decode" | ||||||
|  |                 :disabled="inEdit" | ||||||
|  |                 :format="props.format" | ||||||
|  |                 @format-changed="onFormatChanged" /> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
|   | |||||||
| @@ -163,9 +163,11 @@ watch(() => data.value?.keyPath, initContent) | |||||||
|             <n-button :focusable="false" @click="onReload">{{ $t('interface.reload') }}</n-button> |             <n-button :focusable="false" @click="onReload">{{ $t('interface.reload') }}</n-button> | ||||||
|         </template> |         </template> | ||||||
|     </n-empty> |     </n-empty> | ||||||
|     <keep-alive v-else> |     <!-- FIXME: keep alive may cause virtual list null value error. --> | ||||||
|  |     <!--    <keep-alive v-else>--> | ||||||
|     <component |     <component | ||||||
|         :is="valueComponents[data.type]" |         :is="valueComponents[data.type]" | ||||||
|  |         v-else | ||||||
|         ref="contentRef" |         ref="contentRef" | ||||||
|         :db="data.db" |         :db="data.db" | ||||||
|         :decode="data.decode" |         :decode="data.decode" | ||||||
| @@ -184,7 +186,7 @@ watch(() => data.value?.keyPath, initContent) | |||||||
|         @loadmore="onLoadMore" |         @loadmore="onLoadMore" | ||||||
|         @reload="onReload" |         @reload="onReload" | ||||||
|         @rename="onRename" /> |         @rename="onRename" /> | ||||||
|     </keep-alive> |     <!--    </keep-alive>--> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <style lang="scss" scoped></style> | <style lang="scss" scoped></style> | ||||||
|   | |||||||
| @@ -1200,7 +1200,7 @@ const useBrowserStore = defineStore('browser', { | |||||||
|          * @param {string} format |          * @param {string} format | ||||||
|          * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} |          * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} | ||||||
|          */ |          */ | ||||||
|         async updateListItem({ server, db, key, index, value, decode, format }) { |         async updateListItem({ server, db, key, index, value, decode = decodeTypes.NONE, format = formatTypes.RAW }) { | ||||||
|             try { |             try { | ||||||
|                 const { data, success, msg } = await SetListItem({ server, db, key, index, value, decode, format }) |                 const { data, success, msg } = await SetListItem({ server, db, key, index, value, decode, format }) | ||||||
|                 if (success) { |                 if (success) { | ||||||
| @@ -1266,7 +1266,7 @@ const useBrowserStore = defineStore('browser', { | |||||||
|          */ |          */ | ||||||
|         async addSetItem(connName, db, key, value) { |         async addSetItem(connName, db, key, value) { | ||||||
|             try { |             try { | ||||||
|                 if (!value instanceof Array) { |                 if ((!value) instanceof Array) { | ||||||
|                     value = [value] |                     value = [value] | ||||||
|                 } |                 } | ||||||
|                 const { data, success, msg } = await SetSetItem(connName, db, key, false, value) |                 const { data, success, msg } = await SetSetItem(connName, db, key, false, value) | ||||||
| @@ -1284,20 +1284,23 @@ const useBrowserStore = defineStore('browser', { | |||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * update value of set item |          * update value of set item | ||||||
|          * @param {string} connName |          * @param {string} server | ||||||
|          * @param {number} db |          * @param {number} db | ||||||
|          * @param {string|number[]} key |          * @param {string|number[]} key | ||||||
|          * @param {string} value |          * @param {string|number[]} value | ||||||
|          * @param {string} newValue |          * @param {string|number[]} newValue | ||||||
|          * @returns {Promise<{[msg]: string, success: boolean}>} |          * @param {string} [decode] | ||||||
|  |          * @param {string} [format] | ||||||
|  |          * @returns {Promise<{[msg]: string, success: boolean, [added]: string|number[]}>} | ||||||
|          */ |          */ | ||||||
|         async updateSetItem(connName, db, key, value, newValue) { |         async updateSetItem({ server, db, key, value, newValue, decode = decodeTypes.NONE, format = formatTypes.RAW }) { | ||||||
|             try { |             try { | ||||||
|                 const { success, msg } = await UpdateSetItem(connName, db, key, value, newValue) |                 const { data, success, msg } = await UpdateSetItem({ server, db, key, value, newValue, decode, format }) | ||||||
|                 if (success) { |                 if (success) { | ||||||
|                     const tab = useTabStore() |                     const { added } = data | ||||||
|                     tab.upsertValueEntries({ server: connName, db, key, type: 'set', entries: { [value]: newValue } }) |                     // const tab = useTabStore() | ||||||
|                     return { success: true } |                     // tab.upsertValueEntries({ server, db, key, type: 'set', entries: { [value]: newValue } }) | ||||||
|  |                     return { success: true, added } | ||||||
|                 } else { |                 } else { | ||||||
|                     return { success, msg } |                     return { success, msg } | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -111,6 +111,9 @@ export async function setupDiscreteApi() { | |||||||
|             max: 5, |             max: 5, | ||||||
|             placement: 'bottom-right', |             placement: 'bottom-right', | ||||||
|             keepAliveOnHover: true, |             keepAliveOnHover: true, | ||||||
|  |             containerStyle: { | ||||||
|  |                 marginBottom: '38px', | ||||||
|  |             }, | ||||||
|         }, |         }, | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lykin
					Lykin