feat: support key auto refresh

refactor: move 'ContentToolbar' to 'ContentValueWrapper'
This commit is contained in:
Lykin 2024-01-06 17:14:35 +08:00
parent c2bf4128f7
commit d66d7c9a49
12 changed files with 149 additions and 101 deletions

View File

@ -16,7 +16,6 @@ const emit = defineEmits(['edit', 'delete', 'copy', 'save', 'cancel'])
</script> </script>
<template> <template>
<!-- TODO: support multiple save -->
<div v-if="props.editing" class="flex-box-h edit-column-func"> <div v-if="props.editing" class="flex-box-h edit-column-func">
<icon-button :icon="Save" @click="emit('save')" /> <icon-button :icon="Save" @click="emit('save')" />
<icon-button :icon="Close" @click="emit('cancel')" /> <icon-button :icon="Close" @click="emit('cancel')" />

View File

@ -10,8 +10,10 @@ import { useI18n } from 'vue-i18n'
import IconButton from '@/components/common/IconButton.vue' import IconButton from '@/components/common/IconButton.vue'
import Copy from '@/components/icons/Copy.vue' import Copy from '@/components/icons/Copy.vue'
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js' import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
import { computed } from 'vue' import { computed, onUnmounted, reactive, watch } from 'vue'
import { padStart } from 'lodash' import { padStart } from 'lodash'
import { NIcon, useThemeVars } from 'naive-ui'
import { timeout } from '@/utils/promise.js'
const props = defineProps({ const props = defineProps({
server: String, server: String,
@ -37,6 +39,12 @@ const props = defineProps({
const emit = defineEmits(['reload', 'rename', 'delete']) const emit = defineEmits(['reload', 'rename', 'delete'])
const autoRefresh = reactive({
on: false,
interval: 2,
})
const themeVars = useThemeVars()
const dialogStore = useDialog() const dialogStore = useDialog()
const i18n = useI18n() const i18n = useI18n()
@ -61,6 +69,48 @@ const ttlString = computed(() => {
return s 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 = () => { const onCopyKey = () => {
ClipboardSetText(props.keyPath) ClipboardSetText(props.keyPath)
.then((succ) => { .then((succ) => {
@ -87,14 +137,48 @@ const onTTL = () => {
<div class="content-toolbar flex-box-h"> <div class="content-toolbar flex-box-h">
<n-input-group> <n-input-group>
<redis-type-tag :binary-key="binaryKey" :type="props.keyType" size="large" /> <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> <template #suffix>
<icon-button <n-popover :delay="500" keep-alive-on-hover placement="bottom" trigger="hover">
:icon="Refresh" <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" :loading="props.loading"
size="18" :value="autoRefresh.on"
t-tooltip="interface.reload" @update:value="onToggleRefresh" />
@click="emit('reload')" /> </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> </template>
</n-input> </n-input>
<icon-button :icon="Copy" border size="18" t-tooltip="interface.copy_key" @click="onCopyKey" /> <icon-button :icon="Copy" border size="18" t-tooltip="interface.copy_key" @click="onCopyKey" />
@ -137,4 +221,14 @@ const onTTL = () => {
align-items: center; align-items: center;
gap: 5px; gap: 5px;
} }
.auto-refreshing {
animation: rotate 2s linear infinite;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
</style> </style>

View File

@ -1,7 +1,6 @@
<script setup> <script setup>
import { computed, h, reactive, ref } from 'vue' import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NIcon, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
@ -54,7 +53,7 @@ const props = defineProps({
loading: Boolean, loading: Boolean,
}) })
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match']) const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
/** /**
* *
@ -326,18 +325,7 @@ defineExpose({
<template> <template>
<div class="content-wrapper flex-box-v"> <div class="content-wrapper flex-box-v">
<content-toolbar <slot name="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')" />
<div class="tb2 value-item-part flex-box-h"> <div class="tb2 value-item-part flex-box-h">
<div class="flex-box-h"> <div class="flex-box-h">
<content-search-input <content-search-input

View File

@ -1,7 +1,6 @@
<script setup> <script setup>
import { computed, h, reactive, ref } from 'vue' import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NIcon, useThemeVars } from 'naive-ui'
import { isEmpty, size } from 'lodash' import { isEmpty, size } from 'lodash'
@ -54,7 +53,7 @@ const props = defineProps({
loading: Boolean, loading: Boolean,
}) })
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match']) const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
/** /**
* *
@ -297,18 +296,7 @@ defineExpose({
<template> <template>
<div class="content-wrapper flex-box-v"> <div class="content-wrapper flex-box-v">
<content-toolbar <slot name="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')" />
<div class="tb2 value-item-part flex-box-h"> <div class="tb2 value-item-part flex-box-h">
<div class="flex-box-h"> <div class="flex-box-h">
<content-search-input <content-search-input

View File

@ -1,7 +1,6 @@
<script setup> <script setup>
import { computed, h, reactive, ref } from 'vue' import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NIcon, useThemeVars } from 'naive-ui'
import { isEmpty, size } from 'lodash' import { isEmpty, size } from 'lodash'
@ -53,7 +52,7 @@ const props = defineProps({
loading: Boolean, loading: Boolean,
}) })
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match']) const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
/** /**
* *
@ -294,18 +293,7 @@ defineExpose({
<template> <template>
<div class="content-wrapper flex-box-v"> <div class="content-wrapper flex-box-v">
<content-toolbar <slot name="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')" />
<div class="tb2 value-item-part flex-box-h"> <div class="tb2 value-item-part flex-box-h">
<div class="flex-box-h"> <div class="flex-box-h">
<content-search-input <content-search-input

View File

@ -1,7 +1,6 @@
<script setup> <script setup>
import { computed, h, ref } from 'vue' import { computed, h, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NIcon, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
@ -50,7 +49,7 @@ const props = defineProps({
loading: Boolean, loading: Boolean,
}) })
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match']) const emit = defineEmits(['loadmore', 'loadall', 'match'])
/** /**
* *
@ -177,18 +176,7 @@ defineExpose({
<template> <template>
<div class="content-wrapper flex-box-v"> <div class="content-wrapper flex-box-v">
<content-toolbar <slot name="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')" />
<div class="tb2 value-item-part flex-box-h"> <div class="tb2 value-item-part flex-box-h">
<div class="flex-box-h"> <div class="flex-box-h">
<content-search-input <content-search-input

View File

@ -1,7 +1,6 @@
<script setup> <script setup>
import { computed, reactive, ref, watchEffect } from 'vue' import { computed, reactive, ref, watchEffect } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue'
import Copy from '@/components/icons/Copy.vue' import Copy from '@/components/icons/Copy.vue'
import Save from '@/components/icons/Save.vue' import Save from '@/components/icons/Save.vue'
import { useThemeVars } from 'naive-ui' import { useThemeVars } from 'naive-ui'
@ -34,8 +33,6 @@ const props = defineProps({
loading: Boolean, loading: Boolean,
}) })
const emit = defineEmits(['reload', 'rename', 'delete'])
const i18n = useI18n() const i18n = useI18n()
const themeVars = useThemeVars() const themeVars = useThemeVars()
const prefStore = usePreferencesStore() const prefStore = usePreferencesStore()
@ -172,18 +169,7 @@ defineExpose({
<template> <template>
<div class="content-wrapper flex-box-v"> <div class="content-wrapper flex-box-v">
<content-toolbar <slot name="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')" />
<div class="tb2 value-item-part flex-box-h"> <div class="tb2 value-item-part flex-box-h">
<div class="flex-item-expand"></div> <div class="flex-item-expand"></div>
<n-button-group> <n-button-group>

View File

@ -13,6 +13,7 @@ import { isEmpty } from 'lodash'
import useDialogStore from 'stores/dialog.js' import useDialogStore from 'stores/dialog.js'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js' import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from '@/components/content_value/ContentToolbar.vue'
const themeVars = useThemeVars() const themeVars = useThemeVars()
const browserStore = useBrowserStore() const browserStore = useBrowserStore()
@ -42,6 +43,7 @@ const i18n = useI18n()
* format: String, * format: String,
* decode: String, * decode: String,
* end: Boolean * end: Boolean
* loading: Boolean
* }>} * }>}
*/ */
const data = computed(() => { const data = computed(() => {
@ -49,6 +51,10 @@ const data = computed(() => {
}) })
const initializing = ref(false) const initializing = ref(false)
const loading = computed(() => {
return data.value.loading === true || initializing.value
})
const binaryKey = computed(() => { const binaryKey = computed(() => {
return !!data.value.keyCode return !!data.value.keyCode
}) })
@ -188,7 +194,7 @@ watch(() => data.value?.keyPath, initContent)
:key-code="data.keyCode" :key-code="data.keyCode"
:key-path="data.keyPath" :key-path="data.keyPath"
:length="data.length" :length="data.length"
:loading="data.loading === true || initializing" :loading="loading"
:name="data.name" :name="data.name"
:size="data.size" :size="data.size"
:ttl="data.ttl" :ttl="data.ttl"
@ -197,8 +203,22 @@ watch(() => data.value?.keyPath, initContent)
@loadall="onLoadAll" @loadall="onLoadAll"
@loadmore="onLoadMore" @loadmore="onLoadMore"
@match="onMatch" @match="onMatch"
@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" @reload="onReload"
@rename="onRename" /> @rename="onRename" />
</template>
</component>
<!-- </keep-alive>--> <!-- </keep-alive>-->
</template> </template>

View File

@ -1,7 +1,6 @@
<script setup> <script setup>
import { computed, h, reactive, ref } from 'vue' import { computed, h, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ContentToolbar from './ContentToolbar.vue'
import AddLink from '@/components/icons/AddLink.vue' import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NIcon, useThemeVars } from 'naive-ui' import { NButton, NIcon, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
@ -53,7 +52,7 @@ const props = defineProps({
loading: Boolean, loading: Boolean,
}) })
const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match']) const emit = defineEmits(['loadmore', 'loadall', 'reload', 'match'])
/** /**
* *
@ -331,18 +330,7 @@ defineExpose({
<template> <template>
<div class="content-wrapper flex-box-v"> <div class="content-wrapper flex-box-v">
<content-toolbar <slot name="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')" />
<div class="tb2 value-item-part flex-box-h"> <div class="tb2 value-item-part flex-box-h">
<div class="flex-box-h"> <div class="flex-box-h">
<content-search-input <content-search-input

View File

@ -13,9 +13,10 @@
"minute": "Minutes(s)", "minute": "Minutes(s)",
"hour": "Hour(s)", "hour": "Hour(s)",
"day": "Day(s)", "day": "Day(s)",
"unit_day": "D", "unit_day": "d",
"unit_hour": "H", "unit_hour": "h",
"unit_minute": "M", "unit_minute": "m",
"unit_second": "s",
"all": "All", "all": "All",
"key": "Key", "key": "Key",
"value": "Value", "value": "Value",
@ -108,6 +109,8 @@
"decode_with": "Decode / Decompression", "decode_with": "Decode / Decompression",
"reload": "Reload", "reload": "Reload",
"reload_disable": "Reload will enable after full loaded", "reload_disable": "Reload will enable after full loaded",
"auto_refresh": "Auto Refresh",
"refresh_interval": "Refresh Interval",
"open_connection": "Open Connection", "open_connection": "Open Connection",
"copy_path": "Copy Path", "copy_path": "Copy Path",
"copy_key": "Copy Key", "copy_key": "Copy Key",

View File

@ -10,12 +10,13 @@
"update": "更新", "update": "更新",
"none": "无", "none": "无",
"second": "秒", "second": "秒",
"minute": "分", "minute": "分",
"hour": "小时", "hour": "小时",
"day": "天", "day": "天",
"unit_day": "天", "unit_day": "天",
"unit_hour": "小时", "unit_hour": "小时",
"unit_minute": "分钟", "unit_minute": "分钟",
"unit_second": "秒",
"all": "全部", "all": "全部",
"key": "键", "key": "键",
"value": "值", "value": "值",
@ -108,6 +109,8 @@
"decode_with": "解码/解压方式", "decode_with": "解码/解压方式",
"reload": "重新载入", "reload": "重新载入",
"reload_disable": "全量加载后可重新载入", "reload_disable": "全量加载后可重新载入",
"auto_refresh": "自动刷新",
"refresh_interval": "刷新间隔",
"open_connection": "打开连接", "open_connection": "打开连接",
"copy_path": "复制路径", "copy_path": "复制路径",
"copy_key": "复制键名", "copy_key": "复制键名",

View File

@ -91,6 +91,9 @@ const _darkThemeOverrides = {
Dropdown: { Dropdown: {
color: '#272727', color: '#272727',
}, },
Popover: {
color: '#2C2C32',
},
} }
export const darkThemeOverrides = merge({}, themeOverrides, _darkThemeOverrides) export const darkThemeOverrides = merge({}, themeOverrides, _darkThemeOverrides)