perf: add pin and fullscreen supported for entry editor

This commit is contained in:
Lykin 2023-11-14 22:58:44 +08:00
parent 5b84fa59f6
commit 3c1727db3e
20 changed files with 267 additions and 54 deletions

View File

@ -24,6 +24,7 @@ const props = defineProps({
loading: Boolean,
border: Boolean,
disabled: Boolean,
buttonStyle: [String, Object],
})
const hasTooltip = computed(() => {
@ -39,13 +40,16 @@ const hasTooltip = computed(() => {
:disabled="disabled"
:focusable="false"
:loading="loading"
:style="props.buttonStyle"
:text="!border"
:type="type"
@click.prevent="emit('click')">
<template #icon>
<slot>
<n-icon :color="props.color || 'currentColor'" :size="props.size">
<component :is="props.icon" :stroke-width="props.strokeWidth" />
</n-icon>
</slot>
</template>
</n-button>
</template>
@ -61,9 +65,11 @@ const hasTooltip = computed(() => {
:type="type"
@click.prevent="emit('click')">
<template #icon>
<slot>
<n-icon :color="props.color || 'currentColor'" :size="props.size">
<component :is="props.icon" :stroke-width="props.strokeWidth" />
</n-icon>
</slot>
</template>
</n-button>
</template>

View File

@ -1,11 +1,16 @@
<script setup>
import { computed, defineEmits, defineProps, reactive, ref, watch } from 'vue'
import { computed, defineEmits, defineProps, nextTick, reactive, ref, watch } from 'vue'
import { useThemeVars } from 'naive-ui'
import Save from '@/components/icons/Save.vue'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import { decodeRedisKey } from '@/utils/key_convert.js'
import useBrowserStore from 'stores/browser.js'
import FormatSelector from '@/components/content_value/FormatSelector.vue'
import IconButton from '@/components/common/IconButton.vue'
import FullScreen from '@/components/icons/FullScreen.vue'
import WindowClose from '@/components/icons/WindowClose.vue'
import Pin from '@/components/icons/Pin.vue'
import OffScreen from '@/components/icons/OffScreen.vue'
const props = defineProps({
field: {
@ -29,15 +34,22 @@ const props = defineProps({
fieldReadonly: {
type: Boolean,
},
fullscreen: {
type: Boolean,
},
})
const themeVars = useThemeVars()
const browserStore = useBrowserStore()
const emit = defineEmits(['update:field', 'update:value', 'update:decode', 'update:format', 'save', 'cancel'])
const model = reactive({
field: '',
value: '',
})
const emit = defineEmits([
'update:field',
'update:value',
'update:decode',
'update:format',
'update:fullscreen',
'save',
'close',
])
watch(
() => props.value,
@ -51,6 +63,7 @@ watch(
)
const loading = ref(false)
const pin = ref(false)
const viewAs = reactive({
field: '',
value: '',
@ -67,6 +80,19 @@ const displayValue = computed(() => {
return viewAs.value
})
const btnStyle = computed(() => ({
padding: '3px',
border: 'solid 1px #0000',
borderRadius: '3px',
}))
const pinBtnStyle = computed(() => ({
padding: '3px',
border: `solid 1px ${themeVars.value.borderColor}`,
borderRadius: '3px',
backgroundColor: themeVars.value.borderColor,
}))
/**
*
* @param {string} decode
@ -99,20 +125,26 @@ const onUpdateValue = (value) => {
viewAs.value = value
}
const onToggleFullscreen = () => {
emit('update:fullscreen', !!!props.fullscreen)
}
const onClose = () => {
pin.value = false
emit('close')
}
const onSave = () => {
emit('save', viewAs.field, viewAs.value, viewAs.decode, viewAs.format)
if (!pin.value) {
nextTick().then(onClose)
}
}
</script>
<template>
<div class="entry-editor flex-box-v">
<n-card
:title="$t('interface.edit_row')"
autofocus
closable
size="small"
style="height: 100%"
@close="emit('cancel')">
<n-card :title="$t('interface.edit_row')" autofocus size="small" style="height: 100%">
<div class="editor-content flex-box-v" style="height: 100%">
<!-- field -->
<div class="editor-content-item flex-box-v">
@ -136,14 +168,41 @@ const onSave = () => {
<format-selector
:decode="viewAs.decode"
:format="viewAs.format"
style="margin-top: 5px"
@format-changed="(d, f) => onFormatChanged(d, f)" />
</div>
</div>
<template #header-extra>
<n-space :size="5">
<icon-button
:button-style="pin ? pinBtnStyle : btnStyle"
:size="19"
:t-tooltip="pin ? 'interface.unpin_edit' : 'interface.pin_edit'"
stroke-width="4"
@click="pin = !pin">
<Pin :inverse="pin" stroke-width="4" />
</icon-button>
<icon-button
:button-style="btnStyle"
:icon="props.fullscreen ? OffScreen : FullScreen"
:size="18"
stroke-width="5"
t-tooltip="interface.fullscreen"
@click="onToggleFullscreen" />
<icon-button
:button-style="btnStyle"
:icon="WindowClose"
:size="18"
stroke-width="5"
t-tooltip="menu.close"
@click="onClose" />
</n-space>
</template>
<template #action>
<n-space :wrap="false" :wrap-item="false" justify="end">
<n-button ghost type="primary" @click="onSave">
<n-button ghost @click="onSave">
<template #icon>
<n-icon :component="Save"></n-icon>
<n-icon :component="Save" />
</template>
{{ $t('common.update') }}
</n-button>
@ -181,7 +240,7 @@ const onSave = () => {
background-color: unset;
}
:deep(.n-card--bordered) {
border-radius: 0;
}
//:deep(.n-card--bordered) {
// border-radius: 0;
//}
</style>

View File

@ -88,6 +88,11 @@ const currentEditRow = reactive({
const inEdit = computed(() => {
return currentEditRow.no > 0
})
const fullEdit = ref(false)
const inFullEdit = computed(() => {
return inEdit.value && fullEdit.value
})
const tableRef = ref(null)
const fieldColumn = reactive({
key: 'key',
@ -99,6 +104,7 @@ const fieldColumn = reactive({
tooltip: true,
},
filterOptionValue: null,
className: inEdit ? 'clickable' : '',
filter: (value, row) => {
return !!~row.k.indexOf(value.toString())
},
@ -116,6 +122,7 @@ const valueColumn = reactive({
tooltip: true,
},
filterOptionValue: null,
className: inEdit ? 'clickable' : '',
filter: (value, row) => {
return !!~row.v.indexOf(value.toString())
},
@ -162,8 +169,6 @@ const saveEdit = async (field, value, decode, format) => {
}
} catch (e) {
$message.error(e.message)
} finally {
resetEdit()
}
}
@ -318,6 +323,7 @@ defineExpose({
<template>
<div class="content-wrapper flex-box-v">
<content-toolbar
v-show="!inFullEdit"
:db="props.db"
:key-code="props.keyCode"
:key-path="props.keyPath"
@ -329,7 +335,7 @@ defineExpose({
@delete="emit('delete')"
@reload="emit('reload')"
@rename="emit('rename')" />
<div class="tb2 value-item-part flex-box-h">
<div v-show="!inFullEdit" class="tb2 value-item-part flex-box-h">
<div class="flex-box-h">
<n-input-group>
<n-select
@ -374,6 +380,7 @@ defineExpose({
<!-- table -->
<n-data-table
ref="tableRef"
v-show="!inFullEdit"
:bordered="false"
:bottom-bordered="false"
:columns="columns"
@ -392,6 +399,7 @@ defineExpose({
<!-- edit pane -->
<content-entry-editor
v-show="inEdit"
v-model:fullscreen="fullEdit"
:decode="currentEditRow.decode"
:field="currentEditRow.key"
:field-label="$t('common.field')"
@ -400,7 +408,7 @@ defineExpose({
:value-label="$t('common.value')"
class="flex-item-expand"
style="width: 100%"
@cancel="resetEdit"
@close="resetEdit"
@save="saveEdit" />
</div>
<div class="value-footer flex-box-h">

View File

@ -70,6 +70,11 @@ const currentEditRow = reactive({
const inEdit = computed(() => {
return currentEditRow.no > 0
})
const fullEdit = ref(false)
const inFullEdit = computed(() => {
return inEdit.value && fullEdit.value
})
const valueColumn = reactive({
key: 'value',
title: i18n.t('common.value'),
@ -79,6 +84,7 @@ const valueColumn = reactive({
tooltip: true,
},
filterOptionValue: null,
className: inEdit ? 'clickable' : '',
filter: (value, row) => {
return !!~row.v.indexOf(value.toString())
},
@ -134,8 +140,6 @@ const saveEdit = async (pos, value, decode, format) => {
}
} catch (e) {
$message.error(e.message)
} finally {
resetEdit()
}
}
@ -265,6 +269,7 @@ defineExpose({
<template>
<div class="content-wrapper flex-box-v">
<content-toolbar
v-show="!inFullEdit"
:db="props.db"
:key-code="props.keyCode"
:key-path="props.keyPath"
@ -276,7 +281,7 @@ defineExpose({
@delete="emit('delete')"
@reload="emit('reload')"
@rename="emit('rename')" />
<div class="tb2 value-item-part flex-box-h">
<div v-show="!inFullEdit" class="tb2 value-item-part flex-box-h">
<div class="flex-box-h">
<n-input
v-model:value="filterValue"
@ -312,6 +317,7 @@ defineExpose({
<div class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table -->
<n-data-table
v-show="!inFullEdit"
:bordered="false"
:bottom-bordered="false"
:columns="columns"
@ -330,6 +336,7 @@ defineExpose({
<!-- edit pane -->
<content-entry-editor
v-show="inEdit"
v-model:fullscreen="fullEdit"
:decode="currentEditRow.decode"
:field="currentEditRow.no"
:field-label="$t('common.index')"
@ -339,7 +346,7 @@ defineExpose({
:value-label="$t('common.value')"
class="flex-item-expand"
style="width: 100%"
@cancel="resetEdit"
@close="resetEdit"
@save="saveEdit" />
</div>
<div class="value-footer flex-box-h">

View File

@ -70,6 +70,11 @@ const currentEditRow = reactive({
const inEdit = computed(() => {
return currentEditRow.no > 0
})
const fullEdit = ref(false)
const inFullEdit = computed(() => {
return inEdit.value && fullEdit.value
})
const valueColumn = reactive({
key: 'value',
title: i18n.t('common.value'),
@ -79,6 +84,7 @@ const valueColumn = reactive({
tooltip: true,
},
filterOptionValue: null,
className: inEdit ? 'clickable' : '',
filter: (value, row) => {
return !!~row.v.indexOf(value.toString())
},
@ -134,8 +140,6 @@ const saveEdit = async (pos, value, decode, format) => {
}
} catch (e) {
$message.error(e.message)
} finally {
resetEdit()
}
}
@ -265,6 +269,7 @@ defineExpose({
<template>
<div class="content-wrapper flex-box-v">
<content-toolbar
v-show="!inFullEdit"
:db="props.db"
:key-code="props.keyCode"
:key-path="props.keyPath"
@ -276,7 +281,7 @@ defineExpose({
@delete="emit('delete')"
@reload="emit('reload')"
@rename="emit('rename')" />
<div class="tb2 value-item-part flex-box-h">
<div v-show="!inFullEdit" class="tb2 value-item-part flex-box-h">
<div class="flex-box-h">
<n-input
v-model:value="filterValue"
@ -312,6 +317,7 @@ defineExpose({
<div class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table -->
<n-data-table
v-show="!inFullEdit"
:bordered="false"
:bottom-bordered="false"
:columns="columns"
@ -330,6 +336,7 @@ defineExpose({
<!-- edit pane -->
<content-entry-editor
v-show="inEdit"
v-model:fullscreen="fullEdit"
:decode="currentEditRow.decode"
:field="currentEditRow.no"
:field-label="$t('common.index')"
@ -339,7 +346,7 @@ defineExpose({
:value-label="$t('common.value')"
class="flex-item-expand"
style="width: 100%"
@cancel="resetEdit"
@close="resetEdit"
@save="saveEdit" />
</div>
<div class="value-footer flex-box-h">

View File

@ -36,7 +36,7 @@ const onFormatChanged = (selDecode, selFormat) => {
</script>
<template>
<n-space :size="0" :wrap="false" :wrap-item="false" align="center" justify="start" style="margin-top: 5px">
<n-space :size="0" :wrap="false" :wrap-item="false" align="center" justify="start">
<dropdown-selector
:default="formatTypes.RAW"
:disabled="props.disabled"

View File

@ -1,6 +1,4 @@
<script setup>
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
inverse: {
type: Boolean,

View File

@ -1,6 +1,4 @@
<script setup>
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
inverse: {
type: Boolean,

View File

@ -1,6 +1,4 @@
<script setup>
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
inverse: {
type: Boolean,

View File

@ -1,6 +1,4 @@
<script setup>
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
open: {
type: Boolean,

View File

@ -0,0 +1,39 @@
<script setup>
const props = defineProps({
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="props.strokeWidth"
d="M33 6H42V15"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M42 33V42H33"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M15 42H6V33"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M6 15V6H15"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,39 @@
<script setup>
const props = defineProps({
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="props.strokeWidth"
d="M33 6V15H42"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M15 6V15H6"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M15 42V33H6"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M33 42V33H41.8995"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,32 @@
<script setup>
const props = defineProps({
inverse: {
type: Boolean,
default: false,
},
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#icon-36c2b93b4e0022c)">
<path
:fill="inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth"
d="M10.6963 17.5042C13.3347 14.8657 16.4701 14.9387 19.8781 16.8076L32.62 9.74509L31.8989 4.78683L43.2126 16.1005L38.2656 15.3907L31.1918 28.1214C32.9752 31.7589 33.1337 34.6647 30.4953 37.3032C30.4953 37.3032 26.235 33.0429 22.7171 29.525L6.44305 41.5564L18.4382 25.2461C14.9202 21.7281 10.6963 17.5042 10.6963 17.5042Z"
stroke="currentColor"
stroke-linejoin="round" />
</g>
<defs>
<clipPath id="icon-36c2b93b4e0022c">
<rect fill="#FFF" height="48" width="48" />
</clipPath>
</defs>
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -1,6 +1,4 @@
<script setup>
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
inverse: {
type: Boolean,

View File

@ -1,6 +1,4 @@
<script setup>
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
inverse: {
type: Boolean,

View File

@ -4,13 +4,29 @@ const props = defineProps({
type: [Number, String],
default: 14,
},
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg :height="props.size" :width="props.size" fill="none" viewBox="0 0 48 48">
<path d="M8 8L40 40" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" />
<path d="M8 40L40 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" />
<path
:stroke-width="props.strokeWidth"
d="M8 8L40 40"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4" />
<path
:stroke-width="props.strokeWidth"
d="M8 40L40 8"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4" />
</svg>
</template>

View File

@ -75,6 +75,10 @@
"add_row": "Add Row",
"edit_row": "Edit Row",
"delete_row": "Delete Row",
"fullscreen": "Full Screen",
"offscreen": "Exit Full Screen",
"pin_edit": "Pin Editor(Do not close after save)",
"unpin_edit": "Cancel Pin",
"search": "Search",
"filter_field": "Filter Field",
"filter_value": "Filter Value",

View File

@ -75,6 +75,10 @@
"add_row": "插入行",
"edit_row": "编辑行",
"delete_row": "删除行",
"fullscreen": "全屏显示",
"offscreen": "退出全屏显示",
"pin_edit": "固定编辑框(保存后不关闭)",
"unpin_edit": "取消固定",
"search": "搜索",
"filter_field": "筛选字段",
"filter_value": "筛选值",

View File

@ -51,8 +51,12 @@ body {
flex-grow: 1;
}
.icon-btn {
.clickable {
cursor: pointer;
}
.icon-btn {
@extend .clickable;
line-height: 100%;
}
@ -104,7 +108,7 @@ body {
align-items: center;
gap: 0;
padding: 3px 10px 3px 10px;
min-height: 30px;
height: 30px;
}
}

View File

@ -104,7 +104,7 @@ export async function setupDiscreteApi() {
const { message, dialog, notification } = createDiscreteApi(['message', 'notification', 'dialog'], {
configProviderProps,
messageProviderProps: {
placement: 'bottom-right',
placement: 'bottom',
keepAliveOnHover: true,
},
notificationProviderProps: {