chore: refine ui details

This commit is contained in:
Lykin 2023-11-17 23:50:10 +08:00
parent ddc3868f22
commit 13f343977a
12 changed files with 204 additions and 182 deletions

View File

@ -109,7 +109,7 @@ func autoDecode(str string) (value, resultDecode string) {
// pure digit content may incorrect regard as some encoded type, skip decode // pure digit content may incorrect regard as some encoded type, skip decode
if match, _ := regexp.MatchString(`^\d+$`, str); !match { if match, _ := regexp.MatchString(`^\d+$`, str); !match {
var ok bool var ok bool
if len(str)%4 == 0 && !isSameChar(str) { if len(str)%4 == 0 && len(str) >= 12 && !isSameChar(str) {
if value, ok = decodeBase64(str); ok { if value, ok = decodeBase64(str); ok {
resultDecode = types.DECODE_BASE64 resultDecode = types.DECODE_BASE64
return return

View File

@ -34,39 +34,101 @@ const filterServerOption = computed(() => {
const tableRef = ref(null) const tableRef = ref(null)
const loadHistory = () => { const columns = computed(() => [
data.loading = true {
browserStore title: i18n.t('log.exec_time'),
.getCmdHistory() key: 'timestamp',
.then((list) => { defaultSortOrder: 'ascend',
data.history = list || [] sorter: 'default',
}) width: 180,
.finally(() => { align: 'center',
data.loading = false titleAlign: 'center',
tableRef.value?.scrollTo({ top: 999999 }) render: ({ timestamp }, index) => {
}) return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
},
},
{
title: i18n.t('log.server'),
key: 'server',
filterOptionValue: data.server,
filter: (value, row) => {
return value === '' || row.server === value.toString()
},
width: 150,
align: 'center',
titleAlign: 'center',
ellipsis: {
tooltip: true,
},
},
{
title: i18n.t('log.cmd'),
key: 'cmd',
titleAlign: 'center',
filterOptionValue: data.keyword,
resizable: true,
filter: (value, row) => {
return value === '' || !!~row.cmd.indexOf(value.toString())
},
render: ({ cmd }, index) => {
const cmdList = split(cmd, '\n')
if (size(cmdList) > 1) {
return h(
'div',
null,
map(cmdList, (c) => h('div', null, c)),
)
}
return cmd
},
},
{
title: i18n.t('log.cost_time'),
key: 'cost',
width: 100,
align: 'center',
titleAlign: 'center',
render: ({ cost }, index) => {
const ms = dayjs.duration(cost).asMilliseconds()
if (ms < 1000) {
return `${ms} ms`
} else {
return `${Math.floor(ms / 1000)} s`
}
},
},
])
const loadHistory = async () => {
try {
await nextTick()
data.loading = true
const list = await browserStore.getCmdHistory()
data.history = list || []
} finally {
data.loading = false
tableRef.value?.scrollTo({ top: 999999 })
}
} }
const cleanHistory = async () => { const cleanHistory = async () => {
$dialog.warning(i18n.t('log.confirm_clean_log'), () => { $dialog.warning(i18n.t('log.confirm_clean_log'), async () => {
data.loading = true try {
browserStore data.loading = true
.cleanCmdHistory() const success = await browserStore.cleanCmdHistory()
.then((success) => { if (success) {
if (success) { data.history = []
data.history = [] tableRef.value?.scrollTo({ top: 0 })
tableRef.value?.scrollTo({ top: 0 }) $message.success(i18n.t('common.success'))
$message.success(i18n.t('common.success')) }
} } finally {
}) data.loading = false
.finally(() => { }
data.loading = false
})
}) })
} }
defineExpose({ defineExpose({
refresh: () => nextTick().then(loadHistory), refresh: loadHistory,
}) })
</script> </script>
@ -98,71 +160,12 @@ defineExpose({
<div class="content-value fill-height flex-box-h"> <div class="content-value fill-height flex-box-h">
<n-data-table <n-data-table
ref="tableRef" ref="tableRef"
:columns="[ :columns="columns"
{
title: $t('log.exec_time'),
key: 'timestamp',
defaultSortOrder: 'ascend',
sorter: 'default',
width: 180,
align: 'center',
titleAlign: 'center',
render: ({ timestamp }, index) => {
return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
},
},
{
title: $t('log.server'),
key: 'server',
filterOptionValue: data.server,
filter: (value, row) => {
return value === '' || row.server === value.toString()
},
width: 150,
align: 'center',
titleAlign: 'center',
ellipsis: true,
},
{
title: $t('log.cmd'),
key: 'cmd',
titleAlign: 'center',
filterOptionValue: data.keyword,
resizable: true,
filter: (value, row) => {
return value === '' || !!~row.cmd.indexOf(value.toString())
},
render: ({ cmd }, index) => {
const cmdList = split(cmd, '\n')
if (size(cmdList) > 1) {
return h(
'div',
null,
map(cmdList, (c) => h('div', null, c)),
)
}
return cmd
},
},
{
title: $t('log.cost_time'),
key: 'cost',
width: 100,
align: 'center',
titleAlign: 'center',
render: ({ cost }, index) => {
const ms = dayjs.duration(cost).asMilliseconds()
if (ms < 1000) {
return `${ms} ms`
} else {
return `${Math.floor(ms / 1000)} s`
}
},
},
]"
:data="data.history" :data="data.history"
:loading="data.loading"
class="flex-item-expand" class="flex-item-expand"
flex-height /> flex-height
virtual-scroll />
</div> </div>
</n-card> </n-card>
</template> </template>

View File

@ -151,6 +151,7 @@ const onSave = () => {
<div class="editor-content-item-label">{{ props.fieldLabel }}</div> <div class="editor-content-item-label">{{ props.fieldLabel }}</div>
<n-input <n-input
v-model:value="viewAs.field" v-model:value="viewAs.field"
:placeholder="props.field + ''"
:readonly="props.fieldReadonly" :readonly="props.fieldReadonly"
class="editor-content-item-input" class="editor-content-item-input"
type="text" /> type="text" />
@ -160,6 +161,7 @@ const onSave = () => {
<div class="editor-content-item flex-box-v flex-item-expand"> <div class="editor-content-item flex-box-v flex-item-expand">
<div class="editor-content-item-label">{{ props.valueLabel }}</div> <div class="editor-content-item-label">{{ props.valueLabel }}</div>
<n-input <n-input
:placeholder="props.value"
:resizable="false" :resizable="false"
:value="displayValue" :value="displayValue"
autofocus autofocus

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { h, onMounted, onUnmounted, reactive, ref } from 'vue' import { computed, h, onMounted, onUnmounted, reactive, ref } from 'vue'
import Refresh from '@/components/icons/Refresh.vue' import Refresh from '@/components/icons/Refresh.vue'
import { debounce, isEmpty, map, size, split } from 'lodash' import { debounce, isEmpty, map, size, split } from 'lodash'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -33,6 +33,85 @@ const data = reactive({
const tableRef = ref(null) const tableRef = ref(null)
const columns = computed(() => [
{
title: i18n.t('slog.exec_time'),
key: 'timestamp',
sortOrder: data.sortOrder,
sorter: 'default',
width: 180,
align: 'center',
titleAlign: 'center',
render: ({ timestamp }, index) => {
return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
},
},
{
title: i18n.t('slog.client'),
key: 'client',
filterOptionValue: data.client,
resizable: true,
filter: (value, row) => {
return value === '' || row.client === value.toString() || row.addr === value.toString()
},
width: 200,
align: 'center',
titleAlign: 'center',
ellipsis: {
tooltip: true,
},
render: ({ client, addr }, index) => {
let content = ''
if (!isEmpty(client)) {
content += client
}
if (!isEmpty(addr)) {
if (!isEmpty(content)) {
content += ' - '
}
content += addr
}
return content
},
},
{
title: i18n.t('slog.cmd'),
key: 'cmd',
titleAlign: 'center',
filterOptionValue: data.keyword,
resizable: true,
filter: (value, row) => {
return value === '' || !!~row.cmd.indexOf(value.toString())
},
render: ({ cmd }, index) => {
const cmdList = split(cmd, '\n')
if (size(cmdList) > 1) {
return h(
'div',
null,
map(cmdList, (c) => h('div', null, c)),
)
}
return cmd
},
},
{
title: i18n.t('slog.cost_time'),
key: 'cost',
width: 100,
align: 'center',
titleAlign: 'center',
render: ({ cost }, index) => {
const ms = dayjs.duration(cost).asMilliseconds()
if (ms < 1000) {
return `${ms} ms`
} else {
return `${Math.floor(ms / 1000)} s`
}
},
},
])
const _loadSlowLog = () => { const _loadSlowLog = () => {
data.loading = true data.loading = true
browserStore browserStore
@ -103,86 +182,12 @@ const onListLimitChanged = (limit) => {
<div class="content-value fill-height flex-box-h"> <div class="content-value fill-height flex-box-h">
<n-data-table <n-data-table
ref="tableRef" ref="tableRef"
:columns="[ :columns="columns"
{
title: $t('slog.exec_time'),
key: 'timestamp',
sortOrder: data.sortOrder,
sorter: 'default',
width: 180,
align: 'center',
titleAlign: 'center',
render({ timestamp }, index) {
return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
},
},
{
title: $t('slog.client'),
key: 'client',
filterOptionValue: data.client,
resizable: true,
filter(value, row) {
return value === '' || row.client === value.toString() || row.addr === value.toString()
},
width: 200,
align: 'center',
titleAlign: 'center',
ellipsis: true,
render({ client, addr }, index) {
let content = ''
if (!isEmpty(client)) {
content += client
}
if (!isEmpty(addr)) {
if (!isEmpty(content)) {
content += ' - '
}
content += addr
}
return content
},
},
{
title: $t('slog.cmd'),
key: 'cmd',
titleAlign: 'center',
filterOptionValue: data.keyword,
resizable: true,
width: 100,
filter(value, row) {
return value === '' || !!~row.cmd.indexOf(value.toString())
},
render({ cmd }, index) {
const cmdList = split(cmd, '\n')
if (size(cmdList) > 1) {
return h(
'div',
null,
map(cmdList, (c) => h('div', null, c)),
)
}
return cmd
},
},
{
title: $t('slog.cost_time'),
key: 'cost',
width: 100,
align: 'center',
titleAlign: 'center',
render({ cost }, index) {
const ms = dayjs.duration(cost).asMilliseconds()
if (ms < 1000) {
return `${ms} ms`
} else {
return `${Math.floor(ms / 1000)} s`
}
},
},
]"
:data="data.list" :data="data.list"
:loading="data.loading"
class="flex-item-expand" class="flex-item-expand"
flex-height flex-height
virtual-scroll
@update:sorter="({ order }) => (data.sortOrder = order)" /> @update:sorter="({ order }) => (data.sortOrder = order)" />
</div> </div>
</n-card> </n-card>
@ -190,9 +195,4 @@ const onListLimitChanged = (limit) => {
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/styles/content'; @import '@/styles/content';
.content-container {
padding: 5px;
box-sizing: border-box;
}
</style> </style>

View File

@ -158,6 +158,10 @@ const saveEdit = async (field, value, decode, format) => {
throw new Error('row not exists') throw new Error('row not exists')
} }
if (isEmpty(field)) {
field = currentEditRow.key
}
const { success, msg } = await browserStore.setHash({ const { success, msg } = await browserStore.setHash({
server: props.name, server: props.name,
db: props.db, db: props.db,

View File

@ -130,6 +130,10 @@ const saveEdit = async (pos, value, decode, format) => {
throw new Error('row not exists') throw new Error('row not exists')
} }
if (isEmpty(value)) {
value = currentEditRow.value
}
const { success, msg } = await browserStore.updateListItem({ const { success, msg } = await browserStore.updateListItem({
server: props.name, server: props.name,
db: props.db, db: props.db,

View File

@ -68,7 +68,7 @@ const filterOption = [
}, },
{ {
value: 2, value: 2,
label: i18n.t('interface.score'), label: i18n.t('common.score'),
}, },
] ]
const filterType = ref(1) const filterType = ref(1)
@ -95,7 +95,7 @@ const inFullEdit = computed(() => {
const scoreFilterOption = ref(null) const scoreFilterOption = ref(null)
const scoreColumn = computed(() => ({ const scoreColumn = computed(() => ({
key: 'score', key: 'score',
title: i18n.t('interface.score'), title: i18n.t('common.score'),
align: 'center', align: 'center',
titleAlign: 'center', titleAlign: 'center',
resizable: true, resizable: true,
@ -183,6 +183,10 @@ const saveEdit = async (field, value, decode, format) => {
throw new Error('row not exists') throw new Error('row not exists')
} }
if (isEmpty(value)) {
value = currentEditRow.value
}
const { success, msg } = await browserStore.updateZSetItem({ const { success, msg } = await browserStore.updateZSetItem({
server: props.name, server: props.name,
db: props.db, db: props.db,
@ -429,7 +433,7 @@ defineExpose({
v-model:fullscreen="fullEdit" v-model:fullscreen="fullEdit"
:decode="currentEditRow.decode" :decode="currentEditRow.decode"
:field="currentEditRow.score" :field="currentEditRow.score"
:field-label="$t('interface.score')" :field-label="$t('common.score')"
:format="currentEditRow.format" :format="currentEditRow.format"
:value="currentEditRow.value" :value="currentEditRow.value"
:value-label="$t('common.value')" :value-label="$t('common.value')"

View File

@ -50,7 +50,9 @@ const onUpdate = (val) => {
<n-radio-button v-for="(op, i) in updateOption" :key="i" :label="op.label" :value="op.value" /> <n-radio-button v-for="(op, i) in updateOption" :key="i" :label="op.label" :value="op.value" />
</n-radio-group> </n-radio-group>
</n-form-item> </n-form-item>
<n-form-item :label="$t('dialogue.field.element')" required> <n-form-item
:label="$t('dialogue.field.element') + ' (' + $t('common.field') + ':' + $t('common.value') + ')'"
required>
<n-dynamic-input <n-dynamic-input
v-model:value="kvList" v-model:value="kvList"
:key-placeholder="$t('dialogue.field.enter_field')" :key-placeholder="$t('dialogue.field.enter_field')"

View File

@ -58,14 +58,17 @@ const onUpdate = () => {
<n-radio-button v-for="(op, i) in updateOption" :key="i" :label="op.label" :value="op.value" /> <n-radio-button v-for="(op, i) in updateOption" :key="i" :label="op.label" :value="op.value" />
</n-radio-group> </n-radio-group>
</n-form-item> </n-form-item>
<n-form-item :label="$t('dialogue.field.element')" required> <n-form-item
:label="$t('dialogue.field.element') + ' (' + $t('common.value') + ':' + $t('common.score') + ')'"
required>
<n-dynamic-input v-model:value="zset" @create="onCreate" @update:value="onUpdate"> <n-dynamic-input v-model:value="zset" @create="onCreate" @update:value="onUpdate">
<template #default="{ value }"> <template #default="{ value }">
<n-input <n-input
v-model:value="value.value" v-model:value="value.value"
:placeholder="$t('dialogue.field.enter_elem')" :placeholder="$t('dialogue.field.enter_value')"
type="text" type="text"
@update:value="onUpdate" /> @update:value="onUpdate" />
<n-text>:</n-text>
<n-input-number <n-input-number
v-model:value="value.score" v-model:value="value.score"
:placeholder="$t('dialogue.field.enter_score')" :placeholder="$t('dialogue.field.enter_score')"

View File

@ -17,6 +17,7 @@
"key": "Key", "key": "Key",
"value": "Value", "value": "Value",
"field": "Field", "field": "Field",
"score": "Score",
"index": "Position" "index": "Position"
}, },
"preferences": { "preferences": {
@ -104,7 +105,6 @@
"empty_server_list": "No redis server", "empty_server_list": "No redis server",
"action": "Action", "action": "Action",
"type": "Type", "type": "Type",
"score": "Score",
"cli_welcome": "Welcome to Tiny RDM Redis Console", "cli_welcome": "Welcome to Tiny RDM Redis Console",
"sub_tab": { "sub_tab": {
"status": "Status", "status": "Status",

View File

@ -17,6 +17,7 @@
"key": "Chave", "key": "Chave",
"value": "Valor", "value": "Valor",
"field": "Campo", "field": "Campo",
"score": "Pontuação",
"index": "Posição" "index": "Posição"
}, },
"preferences": { "preferences": {
@ -98,7 +99,6 @@
"empty_server_list": "Nenhum servidor Redis", "empty_server_list": "Nenhum servidor Redis",
"action": "Ação", "action": "Ação",
"type": "Tipo", "type": "Tipo",
"score": "Pontuação",
"cli_welcome": "Bem-vindo ao Console Redis Tiny RDM", "cli_welcome": "Bem-vindo ao Console Redis Tiny RDM",
"sub_tab": { "sub_tab": {
"status": "Status", "status": "Status",

View File

@ -17,6 +17,7 @@
"key": "键", "key": "键",
"value": "值", "value": "值",
"field": "字段", "field": "字段",
"score": "分值",
"index": "位置" "index": "位置"
}, },
"preferences": { "preferences": {
@ -104,7 +105,6 @@
"empty_server_list": "还没添加Redis服务器", "empty_server_list": "还没添加Redis服务器",
"action": "操作", "action": "操作",
"type": "类型", "type": "类型",
"score": "分值",
"cli_welcome": "欢迎使用Tiny RDM的Redis命令行控制台", "cli_welcome": "欢迎使用Tiny RDM的Redis命令行控制台",
"sub_tab": { "sub_tab": {
"status": "状态", "status": "状态",