feat: ReJson/TairJson support

- string.ts: rejsonGet, rejsonSet, rejsonDel using JSON.GET/SET/DEL
- IPC + preload for rejsonGet/rejsonSet
- New ReJsonEditor.vue: Monaco JSON editor with JSON.GET/SET
- KeyDetail: dispatch ReJSON-RL/json/tair-json types to ReJsonEditor
This commit is contained in:
2026-07-07 21:58:17 +08:00
parent 1511427e62
commit b85e67541b
7 changed files with 128 additions and 2 deletions
+2
View File
@@ -11,6 +11,7 @@ import ListEditor from './ListEditor.vue'
import SetEditor from './SetEditor.vue'
import ZsetEditor from './ZsetEditor.vue'
import StreamEditor from './StreamEditor.vue'
import ReJsonEditor from './ReJsonEditor.vue'
const keyStore = useKeyStore()
const connStore = useConnectionStore()
@@ -148,6 +149,7 @@ async function refreshKey() {
<SetEditor v-else-if="keyStore.selectedType === 'set'" />
<ZsetEditor v-else-if="keyStore.selectedType === 'zset'" />
<StreamEditor v-else-if="keyStore.selectedType === 'stream'" />
<ReJsonEditor v-else-if="['ReJSON-RL', 'json', 'tair-json'].includes(keyStore.selectedType || '')" />
<div v-else class="unsupported">
<el-icon><Warning /></el-icon>
<p>{{ t('key.unsupported', { type: keyStore.selectedType }) }}</p>
+90
View File
@@ -0,0 +1,90 @@
<script setup lang="ts">
// src/components/ReJsonEditor.vue
import { ref, watch, computed } from 'vue'
import { VueMonacoEditor } from '@guolao/vue-monaco-editor'
import { useKeyStore } from '@renderer/stores/key'
import { useConnectionStore } from '@renderer/stores/connection'
import { useAppStore } from '@renderer/stores/app'
import { ElMessage } from 'element-plus'
const keyStore = useKeyStore()
const connStore = useConnectionStore()
const appStore = useAppStore()
const editedValue = ref('')
const isEditing = ref(false)
const monacoTheme = computed(() => appStore.theme === 'light' ? 'vs' : 'vs-dark')
const editorOptions = computed(() => ({
readOnly: !isEditing.value,
minimap: { enabled: false },
fontSize: appStore.fontSize,
wordWrap: 'on' as const,
scrollBeyondLastLine: false,
automaticLayout: true,
tabSize: 2,
}))
async function loadValue() {
if (!connStore.activeId || !keyStore.selectedKey) return
try {
const raw = await window.electronAPI.redis.rejsonGet(connStore.activeId, keyStore.selectedKey, '.')
try {
editedValue.value = JSON.stringify(JSON.parse(raw), null, 2)
} catch {
editedValue.value = raw
}
} catch (err: any) {
editedValue.value = ''
}
}
watch(
() => keyStore.selectedKey,
() => { if (keyStore.selectedType === 'ReJSON-RL' || keyStore.selectedType === 'json' || keyStore.selectedType === 'tair-json') loadValue() },
{ immediate: true }
)
async function save() {
if (!connStore.activeId || !keyStore.selectedKey) return
try {
await window.electronAPI.redis.rejsonSet(connStore.activeId, keyStore.selectedKey, '.', editedValue.value)
isEditing.value = false
ElMessage.success('Saved')
} catch (err: any) {
ElMessage.error(err.message)
}
}
</script>
<template>
<div class="rejson-editor">
<div class="editor-toolbar">
<span class="type-badge">ReJSON</span>
<span class="spacer" />
<el-button v-if="!isEditing" size="small" type="primary" @click="isEditing = true">Edit</el-button>
<template v-else>
<el-button size="small" type="primary" @click="save">Save</el-button>
<el-button size="small" @click="isEditing = false; loadValue()">Cancel</el-button>
</template>
</div>
<div class="editor-content">
<vue-monaco-editor
v-model:value="editedValue"
theme="vs-dark"
language="json"
:options="editorOptions"
height="100%"
/>
</div>
</div>
</template>
<style scoped>
.rejson-editor { height: 100%; display: flex; flex-direction: column; }
.editor-toolbar { padding: 12px; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid var(--border-color); flex-shrink: 0; }
.type-badge { font-size: 10px; font-weight: 700; color: var(--accent-light); background: var(--accent-bg); padding: 2px 8px; border-radius: 4px; }
.spacer { flex: 1; }
.editor-content { flex: 1; overflow: hidden; }
</style>