feat: support key auto refresh
refactor: move 'ContentToolbar' to 'ContentValueWrapper'
This commit is contained in:
parent
c2bf4128f7
commit
d66d7c9a49
|
@ -16,7 +16,6 @@ const emit = defineEmits(['edit', 'delete', 'copy', 'save', 'cancel'])
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<!-- TODO: support multiple save -->
|
||||
<div v-if="props.editing" class="flex-box-h edit-column-func">
|
||||
<icon-button :icon="Save" @click="emit('save')" />
|
||||
<icon-button :icon="Close" @click="emit('cancel')" />
|
||||
|
|
|
@ -10,8 +10,10 @@ import { useI18n } from 'vue-i18n'
|
|||
import IconButton from '@/components/common/IconButton.vue'
|
||||
import Copy from '@/components/icons/Copy.vue'
|
||||
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
||||
import { computed } from 'vue'
|
||||
import { computed, onUnmounted, reactive, watch } from 'vue'
|
||||
import { padStart } from 'lodash'
|
||||
import { NIcon, useThemeVars } from 'naive-ui'
|
||||
import { timeout } from '@/utils/promise.js'
|
||||
|
||||
const props = defineProps({
|
||||
server: String,
|
||||
|
@ -37,6 +39,12 @@ const props = defineProps({
|
|||
|
||||
const emit = defineEmits(['reload', 'rename', 'delete'])
|
||||
|
||||
const autoRefresh = reactive({
|
||||
on: false,
|
||||
interval: 2,
|
||||
})
|
||||
|
||||
const themeVars = useThemeVars()
|
||||
const dialogStore = useDialog()
|
||||
const i18n = useI18n()
|
||||
|
||||
|
@ -61,6 +69,48 @@ const ttlString = computed(() => {
|
|||
return s
|
||||
})
|
||||
|
||||
const startAutoRefresh = async () => {
|
||||
if (autoRefresh.on) {
|
||||
return
|
||||
}
|
||||
autoRefresh.on = true
|
||||
let lastExec = Date.now()
|
||||
do {
|
||||
if (!autoRefresh.on) {
|
||||
break
|
||||
}
|
||||
await timeout(100)
|
||||
if (props.loading || Date.now() - lastExec < autoRefresh.interval * 1000) {
|
||||
continue
|
||||
}
|
||||
lastExec = Date.now()
|
||||
emit('reload')
|
||||
} while (true)
|
||||
stopAutoRefresh()
|
||||
}
|
||||
|
||||
const stopAutoRefresh = () => {
|
||||
autoRefresh.on = false
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.keyPath,
|
||||
() => {
|
||||
stopAutoRefresh()
|
||||
autoRefresh.interval = props.interval
|
||||
},
|
||||
)
|
||||
|
||||
onUnmounted(() => stopAutoRefresh())
|
||||
|
||||
const onToggleRefresh = (on) => {
|
||||
if (on) {
|
||||
startAutoRefresh()
|
||||
} else {
|
||||
stopAutoRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
const onCopyKey = () => {
|
||||
ClipboardSetText(props.keyPath)
|
||||
.then((succ) => {
|
||||
|
@ -87,14 +137,48 @@ const onTTL = () => {
|
|||
<div class="content-toolbar flex-box-h">
|
||||
<n-input-group>
|
||||
<redis-type-tag :binary-key="binaryKey" :type="props.keyType" size="large" />
|
||||
<n-input v-model:value="props.keyPath" :title="props.keyPath" readonly>
|
||||
<n-input v-model:value="props.keyPath" :title="props.keyPath" readonly @dblclick="onCopyKey">
|
||||
<template #suffix>
|
||||
<icon-button
|
||||
:icon="Refresh"
|
||||
:loading="props.loading"
|
||||
size="18"
|
||||
t-tooltip="interface.reload"
|
||||
@click="emit('reload')" />
|
||||
<n-popover :delay="500" keep-alive-on-hover placement="bottom" trigger="hover">
|
||||
<template #trigger>
|
||||
<icon-button :loading="props.loading" size="18" @click="emit('reload')">
|
||||
<n-icon :size="props.size">
|
||||
<component
|
||||
:is="Refresh"
|
||||
:class="{ 'auto-refreshing': autoRefresh.on }"
|
||||
:color="autoRefresh.on ? themeVars.primaryColor : undefined"
|
||||
:stroke-width="autoRefresh.on ? 5 : 3" />
|
||||
</n-icon>
|
||||
</icon-button>
|
||||
</template>
|
||||
<n-form
|
||||
:show-feedback="false"
|
||||
label-align="right"
|
||||
label-placement="left"
|
||||
label-width="auto"
|
||||
size="small">
|
||||
<n-form-item :label="$t('interface.auto_refresh')">
|
||||
<n-switch
|
||||
:loading="props.loading"
|
||||
:value="autoRefresh.on"
|
||||
@update:value="onToggleRefresh" />
|
||||
</n-form-item>
|
||||
<n-form-item :label="$t('interface.refresh_interval')">
|
||||
<n-input-number
|
||||
v-model:value="autoRefresh.interval"
|
||||
:autofocus="false"
|
||||
:disabled="autoRefresh.on"
|
||||
:max="9999"
|
||||
:min="1"
|
||||
:show-button="false"
|
||||
style="max-width: 100px">
|
||||
<template #suffix>
|
||||
{{ $t('common.unit_second') }}
|
||||
</template>
|
||||
</n-input-number>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-popover>
|
||||
</template>
|
||||
</n-input>
|
||||
<icon-button :icon="Copy" border size="18" t-tooltip="interface.copy_key" @click="onCopyKey" />
|
||||
|
@ -137,4 +221,14 @@ const onTTL = () => {
|
|||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.auto-refreshing {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
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, NIcon, useThemeVars } from 'naive-ui'
|
||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
|
@ -54,7 +53,7 @@ const props = defineProps({
|
|||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -326,18 +325,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<slot name="toolbar" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<content-search-input
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
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, NIcon, useThemeVars } from 'naive-ui'
|
||||
import { isEmpty, size } from 'lodash'
|
||||
|
@ -54,7 +53,7 @@ const props = defineProps({
|
|||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -297,18 +296,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<slot name="toolbar" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<content-search-input
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
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, NIcon, useThemeVars } from 'naive-ui'
|
||||
import { isEmpty, size } from 'lodash'
|
||||
|
@ -53,7 +52,7 @@ const props = defineProps({
|
|||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -294,18 +293,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<slot name="toolbar" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<content-search-input
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
import { computed, h, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ContentToolbar from './ContentToolbar.vue'
|
||||
import AddLink from '@/components/icons/AddLink.vue'
|
||||
import { NButton, NIcon, useThemeVars } from 'naive-ui'
|
||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
|
@ -50,7 +49,7 @@ const props = defineProps({
|
|||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'match'])
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -177,18 +176,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<slot name="toolbar" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<content-search-input
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
import { computed, reactive, ref, watchEffect } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ContentToolbar from './ContentToolbar.vue'
|
||||
import Copy from '@/components/icons/Copy.vue'
|
||||
import Save from '@/components/icons/Save.vue'
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
|
@ -34,8 +33,6 @@ const props = defineProps({
|
|||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['reload', 'rename', 'delete'])
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
const prefStore = usePreferencesStore()
|
||||
|
@ -172,18 +169,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:key-code="keyCode"
|
||||
:key-path="keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<slot name="toolbar" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group>
|
||||
|
|
|
@ -13,6 +13,7 @@ import { isEmpty } from 'lodash'
|
|||
import useDialogStore from 'stores/dialog.js'
|
||||
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ContentToolbar from '@/components/content_value/ContentToolbar.vue'
|
||||
|
||||
const themeVars = useThemeVars()
|
||||
const browserStore = useBrowserStore()
|
||||
|
@ -42,6 +43,7 @@ const i18n = useI18n()
|
|||
* format: String,
|
||||
* decode: String,
|
||||
* end: Boolean
|
||||
* loading: Boolean
|
||||
* }>}
|
||||
*/
|
||||
const data = computed(() => {
|
||||
|
@ -49,6 +51,10 @@ const data = computed(() => {
|
|||
})
|
||||
const initializing = ref(false)
|
||||
|
||||
const loading = computed(() => {
|
||||
return data.value.loading === true || initializing.value
|
||||
})
|
||||
|
||||
const binaryKey = computed(() => {
|
||||
return !!data.value.keyCode
|
||||
})
|
||||
|
@ -188,7 +194,7 @@ watch(() => data.value?.keyPath, initContent)
|
|||
:key-code="data.keyCode"
|
||||
:key-path="data.keyPath"
|
||||
:length="data.length"
|
||||
:loading="data.loading === true || initializing"
|
||||
:loading="loading"
|
||||
:name="data.name"
|
||||
:size="data.size"
|
||||
:ttl="data.ttl"
|
||||
|
@ -197,8 +203,22 @@ watch(() => data.value?.keyPath, initContent)
|
|||
@loadall="onLoadAll"
|
||||
@loadmore="onLoadMore"
|
||||
@match="onMatch"
|
||||
@reload="onReload"
|
||||
@rename="onRename" />
|
||||
@reload="onReload">
|
||||
<template #toolbar>
|
||||
<content-toolbar
|
||||
:db="data.db"
|
||||
:key-code="data.keyCode"
|
||||
:key-path="data.keyPath"
|
||||
:key-type="data.type"
|
||||
:loading="loading"
|
||||
:server="data.name"
|
||||
:ttl="data.ttl"
|
||||
class="value-item-part"
|
||||
@delete="onDelete"
|
||||
@reload="onReload"
|
||||
@rename="onRename" />
|
||||
</template>
|
||||
</component>
|
||||
<!-- </keep-alive>-->
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
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, NIcon, useThemeVars } from 'naive-ui'
|
||||
import { types, types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
|
@ -53,7 +52,7 @@ const props = defineProps({
|
|||
loading: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match'])
|
||||
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -331,18 +330,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<div class="content-wrapper flex-box-v">
|
||||
<content-toolbar
|
||||
:db="props.db"
|
||||
:key-code="props.keyCode"
|
||||
:key-path="props.keyPath"
|
||||
:key-type="keyType"
|
||||
:loading="props.loading"
|
||||
:server="props.name"
|
||||
:ttl="ttl"
|
||||
class="value-item-part"
|
||||
@delete="emit('delete')"
|
||||
@reload="emit('reload')"
|
||||
@rename="emit('rename')" />
|
||||
<slot name="toolbar" />
|
||||
<div class="tb2 value-item-part flex-box-h">
|
||||
<div class="flex-box-h">
|
||||
<content-search-input
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
"minute": "Minutes(s)",
|
||||
"hour": "Hour(s)",
|
||||
"day": "Day(s)",
|
||||
"unit_day": "D",
|
||||
"unit_hour": "H",
|
||||
"unit_minute": "M",
|
||||
"unit_day": "d",
|
||||
"unit_hour": "h",
|
||||
"unit_minute": "m",
|
||||
"unit_second": "s",
|
||||
"all": "All",
|
||||
"key": "Key",
|
||||
"value": "Value",
|
||||
|
@ -108,6 +109,8 @@
|
|||
"decode_with": "Decode / Decompression",
|
||||
"reload": "Reload",
|
||||
"reload_disable": "Reload will enable after full loaded",
|
||||
"auto_refresh": "Auto Refresh",
|
||||
"refresh_interval": "Refresh Interval",
|
||||
"open_connection": "Open Connection",
|
||||
"copy_path": "Copy Path",
|
||||
"copy_key": "Copy Key",
|
||||
|
|
|
@ -10,12 +10,13 @@
|
|||
"update": "更新",
|
||||
"none": "无",
|
||||
"second": "秒",
|
||||
"minute": "分钟",
|
||||
"minute": "分",
|
||||
"hour": "小时",
|
||||
"day": "天",
|
||||
"unit_day": "天",
|
||||
"unit_hour": "小时",
|
||||
"unit_minute": "分钟",
|
||||
"unit_second": "秒",
|
||||
"all": "全部",
|
||||
"key": "键",
|
||||
"value": "值",
|
||||
|
@ -108,6 +109,8 @@
|
|||
"decode_with": "解码/解压方式",
|
||||
"reload": "重新载入",
|
||||
"reload_disable": "全量加载后可重新载入",
|
||||
"auto_refresh": "自动刷新",
|
||||
"refresh_interval": "刷新间隔",
|
||||
"open_connection": "打开连接",
|
||||
"copy_path": "复制路径",
|
||||
"copy_key": "复制键名",
|
||||
|
|
|
@ -91,6 +91,9 @@ const _darkThemeOverrides = {
|
|||
Dropdown: {
|
||||
color: '#272727',
|
||||
},
|
||||
Popover: {
|
||||
color: '#2C2C32',
|
||||
},
|
||||
}
|
||||
|
||||
export const darkThemeOverrides = merge({}, themeOverrides, _darkThemeOverrides)
|
||||
|
|
Loading…
Reference in New Issue