feat(key-detail): live TTL countdown with expiry check and i18n
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch, onUnmounted } from 'vue'
|
||||||
import { useKeyStore } from '@renderer/stores/key'
|
import { useKeyStore } from '@renderer/stores/key'
|
||||||
import { useConnectionStore } from '@renderer/stores/connection'
|
import { useConnectionStore } from '@renderer/stores/connection'
|
||||||
import { useI18n } from '@renderer/i18n'
|
import { useI18n } from '@renderer/i18n'
|
||||||
@@ -34,13 +34,46 @@ const typeColor = computed(() => {
|
|||||||
return colors[keyStore.selectedType || ''] || 'var(--text-muted)'
|
return colors[keyStore.selectedType || ''] || 'var(--text-muted)'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const liveTTL = ref(keyStore.selectedTTL)
|
||||||
|
|
||||||
|
watch(() => keyStore.selectedTTL, (v) => {
|
||||||
|
liveTTL.value = v
|
||||||
|
})
|
||||||
|
|
||||||
|
let ttlTimer: ReturnType<typeof setInterval> | null = setInterval(async () => {
|
||||||
|
if (liveTTL.value > 0) {
|
||||||
|
liveTTL.value--
|
||||||
|
if (liveTTL.value === 0) {
|
||||||
|
const connId = connStore.activeId
|
||||||
|
const key = keyStore.selectedKey
|
||||||
|
if (!connId || !key) return
|
||||||
|
const exists = await window.electronAPI.redis.existsKey(connId, key)
|
||||||
|
// 用户可能已切换 key,跳过过期检查
|
||||||
|
if (keyStore.selectedKey !== key) return
|
||||||
|
if (!exists) {
|
||||||
|
keyStore.selectedType = 'none'
|
||||||
|
} else {
|
||||||
|
// key 仍存在(TTL 可能被其他客户端刷新),同步实际 TTL
|
||||||
|
const ttl = await window.electronAPI.redis.getKeyTTL(connId, key)
|
||||||
|
if (keyStore.selectedKey !== key) return
|
||||||
|
keyStore.selectedTTL = ttl
|
||||||
|
liveTTL.value = ttl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (ttlTimer) clearInterval(ttlTimer)
|
||||||
|
})
|
||||||
|
|
||||||
const ttlDisplay = computed(() => {
|
const ttlDisplay = computed(() => {
|
||||||
if (keyStore.selectedTTL < 0) return 'No expiry'
|
if (liveTTL.value < 0) return t('key.noExpiry')
|
||||||
if (keyStore.selectedTTL === 0) return 'Expired'
|
if (liveTTL.value === 0) return t('key.expired')
|
||||||
const sec = keyStore.selectedTTL
|
const sec = liveTTL.value
|
||||||
if (sec < 60) return `${sec}s`
|
if (sec < 60) return `${sec}s`
|
||||||
if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`
|
if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`
|
||||||
return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`
|
return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m ${sec % 60}s`
|
||||||
})
|
})
|
||||||
|
|
||||||
function openTtlDialog() {
|
function openTtlDialog() {
|
||||||
@@ -53,6 +86,7 @@ async function saveTtl() {
|
|||||||
try {
|
try {
|
||||||
await window.electronAPI.redis.setTTL(connStore.activeId, keyStore.selectedKey, ttlValue.value)
|
await window.electronAPI.redis.setTTL(connStore.activeId, keyStore.selectedKey, ttlValue.value)
|
||||||
keyStore.selectedTTL = ttlValue.value
|
keyStore.selectedTTL = ttlValue.value
|
||||||
|
liveTTL.value = ttlValue.value
|
||||||
showTtlDialog.value = false
|
showTtlDialog.value = false
|
||||||
ElMessage.success('TTL updated')
|
ElMessage.success('TTL updated')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -246,7 +280,7 @@ async function refreshKey() {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
max-width: 300px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export default {
|
|||||||
ttlTitle: 'TTL festlegen',
|
ttlTitle: 'TTL festlegen',
|
||||||
ttlHint: '-1 = kein Ablauf, 0 = sofort ablaufen',
|
ttlHint: '-1 = kein Ablauf, 0 = sofort ablaufen',
|
||||||
ttlUpdated: 'TTL aktualisiert',
|
ttlUpdated: 'TTL aktualisiert',
|
||||||
|
noExpiry: 'Kein Ablauf',
|
||||||
|
expired: 'Abgelaufen',
|
||||||
exists: 'Schlüssel existiert bereits',
|
exists: 'Schlüssel existiert bereits',
|
||||||
batchDelete: 'Ausgewählte löschen',
|
batchDelete: 'Ausgewählte löschen',
|
||||||
batchConfirm: '{n} Schlüssel löschen?',
|
batchConfirm: '{n} Schlüssel löschen?',
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export default {
|
|||||||
ttlTitle: 'Set TTL',
|
ttlTitle: 'Set TTL',
|
||||||
ttlHint: '-1 = no expiry, 0 = expire now',
|
ttlHint: '-1 = no expiry, 0 = expire now',
|
||||||
ttlUpdated: 'TTL updated',
|
ttlUpdated: 'TTL updated',
|
||||||
|
noExpiry: 'No expiry',
|
||||||
|
expired: 'Expired',
|
||||||
exists: 'Key already exists',
|
exists: 'Key already exists',
|
||||||
batchDelete: 'Delete Selected',
|
batchDelete: 'Delete Selected',
|
||||||
batchConfirm: 'Delete {n} key(s)?',
|
batchConfirm: 'Delete {n} key(s)?',
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export default {
|
|||||||
ttlTitle: 'TTLを設定',
|
ttlTitle: 'TTLを設定',
|
||||||
ttlHint: '-1 = 無期限, 0 = 即時期限切れ',
|
ttlHint: '-1 = 無期限, 0 = 即時期限切れ',
|
||||||
ttlUpdated: 'TTLを更新しました',
|
ttlUpdated: 'TTLを更新しました',
|
||||||
|
noExpiry: '無期限',
|
||||||
|
expired: '期限切れ',
|
||||||
exists: 'キーは既に存在します',
|
exists: 'キーは既に存在します',
|
||||||
batchDelete: '選択を削除',
|
batchDelete: '選択を削除',
|
||||||
batchConfirm: '{n} 個のキーを削除しますか?',
|
batchConfirm: '{n} 個のキーを削除しますか?',
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export default {
|
|||||||
ttlTitle: 'TTL 설정',
|
ttlTitle: 'TTL 설정',
|
||||||
ttlHint: '-1 = 만료 없음, 0 = 즉시 만료',
|
ttlHint: '-1 = 만료 없음, 0 = 즉시 만료',
|
||||||
ttlUpdated: 'TTL이 업데이트되었습니다',
|
ttlUpdated: 'TTL이 업데이트되었습니다',
|
||||||
|
noExpiry: '만료 없음',
|
||||||
|
expired: '만료됨',
|
||||||
exists: '키가 이미 존재합니다',
|
exists: '키가 이미 존재합니다',
|
||||||
batchDelete: '선택 삭제',
|
batchDelete: '선택 삭제',
|
||||||
batchConfirm: '{n}개 키를 삭제하시겠습니까?',
|
batchConfirm: '{n}개 키를 삭제하시겠습니까?',
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export default {
|
|||||||
ttlTitle: '设置 TTL',
|
ttlTitle: '设置 TTL',
|
||||||
ttlHint: '-1 = 永不过期, 0 = 立即过期',
|
ttlHint: '-1 = 永不过期, 0 = 立即过期',
|
||||||
ttlUpdated: 'TTL 已更新',
|
ttlUpdated: 'TTL 已更新',
|
||||||
|
noExpiry: '永不过期',
|
||||||
|
expired: '已过期',
|
||||||
exists: '键已存在',
|
exists: '键已存在',
|
||||||
batchDelete: '删除选中',
|
batchDelete: '删除选中',
|
||||||
batchConfirm: '删除 {n} 个键?',
|
batchConfirm: '删除 {n} 个键?',
|
||||||
|
|||||||
Reference in New Issue
Block a user