feat: add quick function icon button inside item of tree view
This commit is contained in:
parent
2d0e8f5445
commit
0556ff299e
|
@ -32,7 +32,7 @@ const hasTooltip = computed(() => {
|
||||||
<template>
|
<template>
|
||||||
<n-tooltip v-if="hasTooltip">
|
<n-tooltip v-if="hasTooltip">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button :text="!border" :disabled="disabled" @click="emit('click')">
|
<n-button :text="!border" :disabled="disabled" @click.prevent="emit('click')">
|
||||||
<n-icon :size="props.size" :color="props.color">
|
<n-icon :size="props.size" :color="props.color">
|
||||||
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
|
@ -40,7 +40,7 @@ const hasTooltip = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
{{ props.tTooltip ? $t(props.tTooltip) : props.tooltip }}
|
{{ props.tTooltip ? $t(props.tTooltip) : props.tooltip }}
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
<n-button v-else :text="!border" :disabled="disabled" @click="emit('click')">
|
<n-button v-else :text="!border" :disabled="disabled" @click.prevent="emit('click')">
|
||||||
<n-icon :size="props.size" :color="props.color">
|
<n-icon :size="props.size" :color="props.color">
|
||||||
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
|
|
|
@ -80,8 +80,8 @@ const infoFilter = ref('')
|
||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
<n-card>
|
<n-card>
|
||||||
<template #header>
|
<template #header>
|
||||||
{{ props.server }}
|
<n-space align="center" :wrap-item="false" inline size="small">
|
||||||
<n-space inline size="small">
|
{{ props.server }}
|
||||||
<n-tooltip v-if="redisVersion">
|
<n-tooltip v-if="redisVersion">
|
||||||
Redis Version
|
Redis Version
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ConnectionType } from '../../consts/connection_type.js'
|
||||||
import { NIcon, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
|
import { NIcon, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
|
||||||
import Key from '../icons/Key.vue'
|
import Key from '../icons/Key.vue'
|
||||||
import ToggleDb from '../icons/ToggleDb.vue'
|
import ToggleDb from '../icons/ToggleDb.vue'
|
||||||
import { find, get, indexOf, isEmpty, remove } from 'lodash'
|
import { find, get, includes, indexOf, isEmpty, remove } from 'lodash'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import Refresh from '../icons/Refresh.vue'
|
import Refresh from '../icons/Refresh.vue'
|
||||||
import CopyLink from '../icons/CopyLink.vue'
|
import CopyLink from '../icons/CopyLink.vue'
|
||||||
|
@ -22,6 +22,7 @@ import Filter from '../icons/Filter.vue'
|
||||||
import Close from '../icons/Close.vue'
|
import Close from '../icons/Close.vue'
|
||||||
import { typesBgColor, typesColor } from '../../consts/support_redis_type.js'
|
import { typesBgColor, typesColor } from '../../consts/support_redis_type.js'
|
||||||
import useTabStore from '../../stores/tab.js'
|
import useTabStore from '../../stores/tab.js'
|
||||||
|
import IconButton from '../common/IconButton.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -51,7 +52,7 @@ const data = computed(() => {
|
||||||
const dbs = get(connectionStore.databases, props.server, [])
|
const dbs = get(connectionStore.databases, props.server, [])
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: props.server,
|
key: `${props.server}`,
|
||||||
label: props.server,
|
label: props.server,
|
||||||
type: ConnectionType.Server,
|
type: ConnectionType.Server,
|
||||||
children: dbs,
|
children: dbs,
|
||||||
|
@ -76,7 +77,6 @@ const contextMenuParam = reactive({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
options: null,
|
options: null,
|
||||||
currentNode: null,
|
|
||||||
})
|
})
|
||||||
const renderIcon = (icon) => {
|
const renderIcon = (icon) => {
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -87,7 +87,6 @@ const renderIcon = (icon) => {
|
||||||
}
|
}
|
||||||
const menuOptions = {
|
const menuOptions = {
|
||||||
[ConnectionType.Server]: () => {
|
[ConnectionType.Server]: () => {
|
||||||
console.log('open server context')
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: 'server_reload',
|
key: 'server_reload',
|
||||||
|
@ -121,7 +120,7 @@ const menuOptions = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
key: 'd2',
|
key: 'd1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'key_remove',
|
key: 'key_remove',
|
||||||
|
@ -130,7 +129,7 @@ const menuOptions = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
key: 'd1',
|
key: 'd2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'db_close',
|
key: 'db_close',
|
||||||
|
@ -244,7 +243,7 @@ const onUpdateSelectedKeys = (keys, options) => {
|
||||||
for (const node of options) {
|
for (const node of options) {
|
||||||
if (node.type === ConnectionType.RedisValue) {
|
if (node.type === ConnectionType.RedisValue) {
|
||||||
const { key, db, redisKey } = node
|
const { key, db, redisKey } = node
|
||||||
if (indexOf(selectedKeys.value, key) === -1) {
|
if (!includes(selectedKeys.value, key)) {
|
||||||
connectionStore.loadKeyValue(props.server, db, redisKey)
|
connectionStore.loadKeyValue(props.server, db, redisKey)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -296,9 +295,57 @@ const renderPrefix = ({ option }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render tree item label
|
||||||
const renderLabel = ({ option }) => {
|
const renderLabel = ({ option }) => {
|
||||||
switch (option.type) {
|
switch (option.type) {
|
||||||
case ConnectionType.RedisDB:
|
case ConnectionType.RedisDB:
|
||||||
|
const { name: server, db } = option
|
||||||
|
let { match: matchPattern, type: typeFilter } = connectionStore.getKeyFilter(server, db)
|
||||||
|
const items = [`${option.label} (${option.keys || 0})`]
|
||||||
|
// show filter tag after label
|
||||||
|
// type filter tag
|
||||||
|
if (!isEmpty(typeFilter)) {
|
||||||
|
items.push(
|
||||||
|
h(
|
||||||
|
NTag,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
closable: true,
|
||||||
|
bordered: false,
|
||||||
|
color: {
|
||||||
|
color: typesBgColor[typeFilter],
|
||||||
|
textColor: typesColor[typeFilter],
|
||||||
|
},
|
||||||
|
onClose: () => {
|
||||||
|
// remove type filter
|
||||||
|
connectionStore.setKeyFilter(server, db, matchPattern)
|
||||||
|
connectionStore.reopenDatabase(server, db)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => typeFilter },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// match pattern tag
|
||||||
|
if (!isEmpty(matchPattern) && matchPattern !== '*') {
|
||||||
|
items.push(
|
||||||
|
h(
|
||||||
|
NTag,
|
||||||
|
{
|
||||||
|
bordered: false,
|
||||||
|
closable: true,
|
||||||
|
size: 'small',
|
||||||
|
onClose: () => {
|
||||||
|
// remove key match pattern
|
||||||
|
connectionStore.setKeyFilter(server, db, '*', typeFilter)
|
||||||
|
connectionStore.reopenDatabase(server, db)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => matchPattern },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return renderIconMenu(items)
|
||||||
case ConnectionType.RedisKey:
|
case ConnectionType.RedisKey:
|
||||||
return `${option.label} (${option.keys || 0})`
|
return `${option.label} (${option.keys || 0})`
|
||||||
// case ConnectionType.RedisValue:
|
// case ConnectionType.RedisValue:
|
||||||
|
@ -307,55 +354,99 @@ const renderLabel = ({ option }) => {
|
||||||
return option.label
|
return option.label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render horizontal item
|
||||||
|
const renderIconMenu = (items) => {
|
||||||
|
return h(
|
||||||
|
NSpace,
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
inline: true,
|
||||||
|
size: 2,
|
||||||
|
wrapItem: false,
|
||||||
|
wrap: false,
|
||||||
|
style: 'margin-right: 5px',
|
||||||
|
},
|
||||||
|
() => items,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDatabaseMenu = (opened) => {
|
||||||
|
const btns = []
|
||||||
|
if (opened) {
|
||||||
|
btns.push(
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'filter_key',
|
||||||
|
icon: Filter,
|
||||||
|
onClick: () => handleSelectContextMenu('db_filter'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'reload',
|
||||||
|
icon: Refresh,
|
||||||
|
onClick: () => handleSelectContextMenu('db_reload'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'new_key',
|
||||||
|
icon: Add,
|
||||||
|
onClick: () => handleSelectContextMenu('db_newkey'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'batch_delete',
|
||||||
|
icon: Delete,
|
||||||
|
onClick: () => handleSelectContextMenu('key_remove'),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
btns.push(
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'open_db',
|
||||||
|
icon: Connect,
|
||||||
|
onClick: () => handleSelectContextMenu('db_open'),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return btns
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLayerMenu = () => {
|
||||||
|
return [
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'reload',
|
||||||
|
icon: Refresh,
|
||||||
|
onClick: () => handleSelectContextMenu('key_reload'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'new_key',
|
||||||
|
icon: Add,
|
||||||
|
onClick: () => handleSelectContextMenu('key_newkey'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'batch_delete',
|
||||||
|
icon: Delete,
|
||||||
|
onClick: () => handleSelectContextMenu('key_remove'),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getValueMenu = () => {
|
||||||
|
return [
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'remove_key',
|
||||||
|
icon: Delete,
|
||||||
|
onClick: () => handleSelectContextMenu('value_remove'),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// render menu function icon
|
||||||
const renderSuffix = ({ option }) => {
|
const renderSuffix = ({ option }) => {
|
||||||
if (option.type === ConnectionType.RedisDB) {
|
if (includes(selectedKeys.value, option.key)) {
|
||||||
const { name: server, db } = option
|
switch (option.type) {
|
||||||
let { match: matchPattern, type: typeFilter } = connectionStore.getKeyFilter(server, db)
|
case ConnectionType.RedisDB:
|
||||||
const filterNodes = []
|
return renderIconMenu(getDatabaseMenu(option.opened))
|
||||||
// type filter tag
|
case ConnectionType.RedisKey:
|
||||||
if (!isEmpty(typeFilter)) {
|
return renderIconMenu(getLayerMenu())
|
||||||
filterNodes.push(
|
case ConnectionType.RedisValue:
|
||||||
h(
|
return renderIconMenu(getValueMenu())
|
||||||
NTag,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
closable: true,
|
|
||||||
bordered: false,
|
|
||||||
color: {
|
|
||||||
color: typesBgColor[typeFilter],
|
|
||||||
textColor: typesColor[typeFilter],
|
|
||||||
},
|
|
||||||
onClose: () => {
|
|
||||||
// remove type filter
|
|
||||||
connectionStore.setKeyFilter(server, db, matchPattern)
|
|
||||||
connectionStore.reopenDatabase(server, db)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ default: () => typeFilter },
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// match pattern tag
|
|
||||||
if (!isEmpty(matchPattern) && matchPattern !== '*') {
|
|
||||||
filterNodes.push(
|
|
||||||
h(
|
|
||||||
NTag,
|
|
||||||
{
|
|
||||||
bordered: false,
|
|
||||||
closable: true,
|
|
||||||
size: 'small',
|
|
||||||
onClose: () => {
|
|
||||||
// remove key match pattern
|
|
||||||
connectionStore.setKeyFilter(server, db, '*', typeFilter)
|
|
||||||
connectionStore.reopenDatabase(server, db)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ default: () => matchPattern },
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (filterNodes.length > 0) {
|
|
||||||
return h(NSpace, { align: 'center', inline: true, size: 2 }, () => filterNodes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -363,7 +454,7 @@ const renderSuffix = ({ option }) => {
|
||||||
|
|
||||||
const nodeProps = ({ option }) => {
|
const nodeProps = ({ option }) => {
|
||||||
return {
|
return {
|
||||||
onDblclick: async () => {
|
onDblclick: () => {
|
||||||
if (loading.value) {
|
if (loading.value) {
|
||||||
console.warn('TODO: alert to ignore double click when loading')
|
console.warn('TODO: alert to ignore double click when loading')
|
||||||
return
|
return
|
||||||
|
@ -373,14 +464,12 @@ const nodeProps = ({ option }) => {
|
||||||
},
|
},
|
||||||
onContextmenu(e) {
|
onContextmenu(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const mop = menuOptions[option.type]
|
if (!menuOptions.hasOwnProperty(option.type)) {
|
||||||
if (mop == null) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
contextMenuParam.show = false
|
contextMenuParam.show = false
|
||||||
|
contextMenuParam.options = menuOptions[option.type](option)
|
||||||
nextTick().then(() => {
|
nextTick().then(() => {
|
||||||
contextMenuParam.options = mop(option)
|
|
||||||
contextMenuParam.currentNode = option
|
|
||||||
contextMenuParam.x = e.clientX
|
contextMenuParam.x = e.clientX
|
||||||
contextMenuParam.y = e.clientY
|
contextMenuParam.y = e.clientY
|
||||||
contextMenuParam.show = true
|
contextMenuParam.show = true
|
||||||
|
@ -419,7 +508,12 @@ const onLoadTree = async (node) => {
|
||||||
const confirmDialog = useConfirmDialog()
|
const confirmDialog = useConfirmDialog()
|
||||||
const handleSelectContextMenu = (key) => {
|
const handleSelectContextMenu = (key) => {
|
||||||
contextMenuParam.show = false
|
contextMenuParam.show = false
|
||||||
const { db, key: nodeKey, redisKey } = contextMenuParam.currentNode
|
const selectedKey = get(selectedKeys.value, 0)
|
||||||
|
if (selectedKey == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const node = connectionStore.getNode(selectedKey)
|
||||||
|
const { db, key: nodeKey, redisKey } = node || {}
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'server_reload':
|
case 'server_reload':
|
||||||
connectionStore.openConnection(props.server, true).then(() => {
|
connectionStore.openConnection(props.server, true).then(() => {
|
||||||
|
|
|
@ -797,6 +797,28 @@ const useConnectionStore = defineStore('connections', {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get tree node by key name
|
||||||
|
* @param key
|
||||||
|
* @return {DatabaseItem|null}
|
||||||
|
*/
|
||||||
|
getNode(key) {
|
||||||
|
const matches = key.match(/^(?<server>\w+)(?:\/db(?<db>\d+))?(?:#(?<key>[\w/]+))?$/)
|
||||||
|
if (matches) {
|
||||||
|
const { server, db, key } = matches.groups
|
||||||
|
if (db != null) {
|
||||||
|
const dbIndex = parseInt(db)
|
||||||
|
const nodeMap = this._getNodeMap(server, dbIndex)
|
||||||
|
if (key != null) {
|
||||||
|
return nodeMap.get(key)
|
||||||
|
} else {
|
||||||
|
return this.databases[server][dbIndex]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set redis key
|
* set redis key
|
||||||
* @param {string} connName
|
* @param {string} connName
|
||||||
|
|
Loading…
Reference in New Issue