feat: add icon function buttons to connection tree items
fix: error cause when close non-connected connection
This commit is contained in:
parent
9fc0cbd40d
commit
484e6a5f6b
|
@ -4,5 +4,6 @@
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"bracketSameLine": true,
|
"bracketSameLine": true,
|
||||||
"endOfLine": "auto"
|
"endOfLine": "auto",
|
||||||
|
"htmlWhitespaceSensitivity": "ignore"
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,6 @@ const valueComponents = {
|
||||||
[types.STREAM]: ContentValueStream,
|
[types.STREAM]: ContentValueStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialog = useDialog()
|
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
const tabStore = useTabStore()
|
const tabStore = useTabStore()
|
||||||
const tab = computed(() =>
|
const tab = computed(() =>
|
||||||
|
|
|
@ -134,7 +134,6 @@ const infoFilter = ref('')
|
||||||
<n-statistic :value="totalKeys">
|
<n-statistic :value="totalKeys">
|
||||||
<template #label>
|
<template #label>
|
||||||
{{ $t('total_keys') }}
|
{{ $t('total_keys') }}
|
||||||
<n-icon :component="Help" />
|
|
||||||
</template>
|
</template>
|
||||||
</n-statistic>
|
</n-statistic>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, h, nextTick, onMounted, reactive, ref } from 'vue'
|
import { computed, h, nextTick, onMounted, reactive, ref } from 'vue'
|
||||||
import { ConnectionType } from '@/consts/connection_type.js'
|
import { ConnectionType } from '@/consts/connection_type.js'
|
||||||
import { NIcon, NSpace, NTag, useDialog } from 'naive-ui'
|
import { NIcon, NSpace, NTag } from 'naive-ui'
|
||||||
import Key from '@/components/icons/Key.vue'
|
import Key from '@/components/icons/Key.vue'
|
||||||
import ToggleDb from '@/components/icons/ToggleDb.vue'
|
import ToggleDb from '@/components/icons/ToggleDb.vue'
|
||||||
import { find, get, includes, indexOf, isEmpty, remove } from 'lodash'
|
import { find, get, includes, indexOf, isEmpty, remove } from 'lodash'
|
||||||
|
@ -22,6 +22,7 @@ import Close from '@/components/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 '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
|
import { parseHexColor } from '@/utils/rgb.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -61,13 +62,7 @@ const data = computed(() => {
|
||||||
|
|
||||||
const backgroundColor = computed(() => {
|
const backgroundColor = computed(() => {
|
||||||
const { markColor: hex = '' } = connectionStore.serverProfile[props.server] || {}
|
const { markColor: hex = '' } = connectionStore.serverProfile[props.server] || {}
|
||||||
if (isEmpty(hex)) {
|
const { r, g, b } = parseHexColor(hex)
|
||||||
return ''
|
|
||||||
}
|
|
||||||
const bigint = parseInt(hex.slice(1), 16)
|
|
||||||
const r = (bigint >> 16) & 255
|
|
||||||
const g = (bigint >> 8) & 255
|
|
||||||
const b = bigint & 255
|
|
||||||
return `rgba(${r}, ${g}, ${b}, 0.2)`
|
return `rgba(${r}, ${g}, ${b}, 0.2)`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -292,7 +287,6 @@ defineExpose({
|
||||||
handleSelectContextMenu,
|
handleSelectContextMenu,
|
||||||
})
|
})
|
||||||
|
|
||||||
const dialog = useDialog()
|
|
||||||
const onUpdateExpanded = (value, option, meta) => {
|
const onUpdateExpanded = (value, option, meta) => {
|
||||||
expandedKeys.value = value
|
expandedKeys.value = value
|
||||||
if (!meta.node) {
|
if (!meta.node) {
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
import useDialogStore from 'stores/dialog.js'
|
import useDialogStore from 'stores/dialog.js'
|
||||||
import { h, nextTick, reactive, ref } from 'vue'
|
import { h, nextTick, reactive, ref } from 'vue'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
import { NIcon, useDialog, useThemeVars } from 'naive-ui'
|
import { NIcon, NSpace, NText, useThemeVars } from 'naive-ui'
|
||||||
import { ConnectionType } from '@/consts/connection_type.js'
|
import { ConnectionType } from '@/consts/connection_type.js'
|
||||||
import ToggleFolder from '@/components/icons/ToggleFolder.vue'
|
import ToggleFolder from '@/components/icons/ToggleFolder.vue'
|
||||||
import ToggleServer from '@/components/icons/ToggleServer.vue'
|
import ToggleServer from '@/components/icons/ToggleServer.vue'
|
||||||
import { debounce, indexOf, isEmpty } from 'lodash'
|
import { debounce, get, includes, indexOf, isEmpty, split } from 'lodash'
|
||||||
import Config from '@/components/icons/Config.vue'
|
import Config from '@/components/icons/Config.vue'
|
||||||
import Delete from '@/components/icons/Delete.vue'
|
import Delete from '@/components/icons/Delete.vue'
|
||||||
import Unlink from '@/components/icons/Unlink.vue'
|
import Unlink from '@/components/icons/Unlink.vue'
|
||||||
|
@ -15,6 +15,8 @@ import Connect from '@/components/icons/Connect.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import useTabStore from 'stores/tab.js'
|
import useTabStore from 'stores/tab.js'
|
||||||
import Edit from '@/components/icons/Edit.vue'
|
import Edit from '@/components/icons/Edit.vue'
|
||||||
|
import { hexGammaCorrection, parseHexColor, toHexColor } from '@/utils/rgb.js'
|
||||||
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
|
@ -116,9 +118,41 @@ const menuOptions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderLabel = ({ option }) => {
|
const renderLabel = ({ option }) => {
|
||||||
|
if (option.type === ConnectionType.Server) {
|
||||||
|
const { markColor = '' } = connectionStore.serverProfile[option.name] || {}
|
||||||
|
if (!isEmpty(markColor)) {
|
||||||
|
const rgb = parseHexColor(markColor)
|
||||||
|
const rgb2 = hexGammaCorrection(rgb, 0.75)
|
||||||
|
return h(
|
||||||
|
NText,
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
color: toHexColor(rgb2),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
() => option.label,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
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 renderPrefix = ({ option }) => {
|
const renderPrefix = ({ option }) => {
|
||||||
switch (option.type) {
|
switch (option.type) {
|
||||||
case ConnectionType.Group:
|
case ConnectionType.Group:
|
||||||
|
@ -142,21 +176,67 @@ const renderPrefix = ({ option }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderSuffix = ({ option }) => {
|
const getServerMenu = (connected) => {
|
||||||
if (option.type === ConnectionType.Server) {
|
const btns = []
|
||||||
const { markColor = '' } = connectionStore.serverProfile[option.name] || {}
|
if (connected) {
|
||||||
if (isEmpty(markColor)) {
|
btns.push(
|
||||||
return ''
|
h(IconButton, {
|
||||||
|
tTooltip: 'disconnect',
|
||||||
|
icon: Unlink,
|
||||||
|
onClick: () => handleSelectContextMenu('server_close'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'edit_conn',
|
||||||
|
icon: Config,
|
||||||
|
onClick: () => handleSelectContextMenu('server_edit'),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
btns.push(
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'open_connection',
|
||||||
|
icon: Connect,
|
||||||
|
onClick: () => handleSelectContextMenu('server_open'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'edit_conn',
|
||||||
|
icon: Config,
|
||||||
|
onClick: () => handleSelectContextMenu('server_edit'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'remove_conn',
|
||||||
|
icon: Delete,
|
||||||
|
onClick: () => handleSelectContextMenu('server_remove'),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return btns
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGroupMenu = () => {
|
||||||
|
return [
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'edit_conn',
|
||||||
|
icon: Config,
|
||||||
|
onClick: () => handleSelectContextMenu('group_rename'),
|
||||||
|
}),
|
||||||
|
h(IconButton, {
|
||||||
|
tTooltip: 'remove_conn',
|
||||||
|
icon: Delete,
|
||||||
|
onClick: () => handleSelectContextMenu('group_delete'),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderSuffix = ({ option }) => {
|
||||||
|
if (includes(selectedKeys.value, option.key)) {
|
||||||
|
switch (option.type) {
|
||||||
|
case ConnectionType.Server:
|
||||||
|
const connected = connectionStore.isConnected(option.name)
|
||||||
|
return renderIconMenu(getServerMenu(connected))
|
||||||
|
case ConnectionType.Group:
|
||||||
|
return renderIconMenu(getGroupMenu())
|
||||||
}
|
}
|
||||||
return h('div', {
|
|
||||||
style: {
|
|
||||||
borderRadius: '50%',
|
|
||||||
backgroundColor: markColor,
|
|
||||||
width: '13px',
|
|
||||||
height: '13px',
|
|
||||||
border: '2px solid white',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -191,7 +271,6 @@ const openConnection = async (name) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialog = useDialog()
|
|
||||||
const removeConnection = (name) => {
|
const removeConnection = (name) => {
|
||||||
$dialog.warning(i18n.t('remove_tip', { type: i18n.t('conn_name'), name }), async () => {
|
$dialog.warning(i18n.t('remove_tip', { type: i18n.t('conn_name'), name }), async () => {
|
||||||
connectionStore.deleteConnection(name).then(({ success, msg }) => {
|
connectionStore.deleteConnection(name).then(({ success, msg }) => {
|
||||||
|
@ -203,7 +282,7 @@ const removeConnection = (name) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeGroup = async (name) => {
|
const removeGroup = async (name) => {
|
||||||
$dialog.warning(i18n.t('remove_tip', { type: i18n.t('conn_group'), name }), async () => {
|
$dialog.warning(i18n.t('remove_group_tip', { name }), async () => {
|
||||||
connectionStore.deleteGroup(name).then(({ success, msg }) => {
|
connectionStore.deleteGroup(name).then(({ success, msg }) => {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
$message.error(msg)
|
$message.error(msg)
|
||||||
|
@ -256,7 +335,14 @@ const renderContextLabel = (option) => {
|
||||||
|
|
||||||
const handleSelectContextMenu = (key) => {
|
const handleSelectContextMenu = (key) => {
|
||||||
contextMenuParam.show = false
|
contextMenuParam.show = false
|
||||||
const { name, label, db, key: nodeKey, redisKey } = contextMenuParam.currentNode
|
const selectedKey = get(selectedKeys.value, 0)
|
||||||
|
if (selectedKey == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const [group, name] = split(selectedKey, '/')
|
||||||
|
if (isEmpty(group) && isEmpty(name)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'server_open':
|
case 'server_open':
|
||||||
openConnection(name).then(() => {})
|
openConnection(name).then(() => {})
|
||||||
|
@ -276,13 +362,17 @@ const handleSelectContextMenu = (key) => {
|
||||||
removeConnection(name)
|
removeConnection(name)
|
||||||
break
|
break
|
||||||
case 'server_close':
|
case 'server_close':
|
||||||
|
if (!isEmpty(group)) {
|
||||||
connectionStore.closeConnection(name)
|
connectionStore.closeConnection(name)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 'group_rename':
|
case 'group_rename':
|
||||||
dialogStore.openRenameGroupDialog(label)
|
if (!isEmpty(group)) {
|
||||||
|
dialogStore.openRenameGroupDialog(group)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 'group_delete':
|
case 'group_delete':
|
||||||
removeGroup(label)
|
removeGroup(group)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.warn('TODO: handle context menu:' + key)
|
console.warn('TODO: handle context menu:' + key)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"edit_close_confirm": "Please close the relevant connections before editing. Do you want to continue?",
|
"edit_close_confirm": "Please close the relevant connections before editing. Do you want to continue?",
|
||||||
"opening_connection": "Opening Connection...",
|
"opening_connection": "Opening Connection...",
|
||||||
"remove_tip": "{type} \"{name}\" will be deleted",
|
"remove_tip": "{type} \"{name}\" will be deleted",
|
||||||
|
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
|
||||||
"ttl": "TTL",
|
"ttl": "TTL",
|
||||||
"forever": "Forever",
|
"forever": "Forever",
|
||||||
"rename_key": "Rename Key",
|
"rename_key": "Rename Key",
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"edit_close_confirm": "编辑前需要关闭相关连接,是否继续",
|
"edit_close_confirm": "编辑前需要关闭相关连接,是否继续",
|
||||||
"opening_connection": "正在打开连接...",
|
"opening_connection": "正在打开连接...",
|
||||||
"remove_tip": "{type} \"{name}\" 将会被删除",
|
"remove_tip": "{type} \"{name}\" 将会被删除",
|
||||||
|
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
|
||||||
"ttl": "TTL",
|
"ttl": "TTL",
|
||||||
"forever": "永久",
|
"forever": "永久",
|
||||||
"rename_key": "重命名键",
|
"rename_key": "重命名键",
|
||||||
|
|
|
@ -124,7 +124,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
if (conn.type !== 'group') {
|
if (conn.type !== 'group') {
|
||||||
// top level
|
// top level
|
||||||
conns.push({
|
conns.push({
|
||||||
key: conn.name,
|
key: '/' + conn.name,
|
||||||
label: conn.name,
|
label: conn.name,
|
||||||
name: conn.name,
|
name: conn.name,
|
||||||
type: ConnectionType.Server,
|
type: ConnectionType.Server,
|
||||||
|
@ -151,7 +151,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
conns.push({
|
conns.push({
|
||||||
key: conn.name,
|
key: conn.name + '/',
|
||||||
label: conn.name,
|
label: conn.name,
|
||||||
type: ConnectionType.Group,
|
type: ConnectionType.Group,
|
||||||
children,
|
children,
|
||||||
|
@ -346,10 +346,12 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbs = this.databases[name]
|
const dbs = this.databases[name]
|
||||||
|
if (!isEmpty(dbs)) {
|
||||||
for (const db of dbs) {
|
for (const db of dbs) {
|
||||||
this.removeKeyFilter(name, db.db)
|
this.removeKeyFilter(name, db.db)
|
||||||
this._getNodeMap(name, db.db).clear()
|
this._getNodeMap(name, db.db).clear()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.removeKeyFilter(name, -1)
|
this.removeKeyFilter(name, -1)
|
||||||
delete this.databases[name]
|
delete this.databases[name]
|
||||||
delete this.serverStats[name]
|
delete this.serverStats[name]
|
||||||
|
|
|
@ -76,6 +76,7 @@ const useDialogStore = defineStore('dialog', {
|
||||||
},
|
},
|
||||||
|
|
||||||
openNewGroupDialog() {
|
openNewGroupDialog() {
|
||||||
|
this.editGroup = ''
|
||||||
this.groupDialogVisible = true
|
this.groupDialogVisible = true
|
||||||
},
|
},
|
||||||
closeNewGroupDialog() {
|
closeNewGroupDialog() {
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { padStart, size, startsWith } from 'lodash'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} RGB
|
||||||
|
* @property {number} r
|
||||||
|
* @property {number} g
|
||||||
|
* @property {number} b
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse hex color to rgb object
|
||||||
|
* @param hex
|
||||||
|
* @return {RGB}
|
||||||
|
*/
|
||||||
|
export function parseHexColor(hex) {
|
||||||
|
if (size(hex) < 6) {
|
||||||
|
return { r: 0, g: 0, b: 0 }
|
||||||
|
}
|
||||||
|
if (startsWith(hex, '#')) {
|
||||||
|
hex = hex.slice(1)
|
||||||
|
}
|
||||||
|
const bigint = parseInt(hex, 16)
|
||||||
|
const r = (bigint >> 16) & 255
|
||||||
|
const g = (bigint >> 8) & 255
|
||||||
|
const b = bigint & 255
|
||||||
|
return { r, g, b }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do gamma correction with an RGB object
|
||||||
|
* @param {RGB} rgb
|
||||||
|
* @param {Number} gamma
|
||||||
|
* @return {RGB}
|
||||||
|
*/
|
||||||
|
export function hexGammaCorrection(rgb, gamma) {
|
||||||
|
if (typeof rgb !== 'object') {
|
||||||
|
return { r: 0, g: 0, b: 0 }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
r: Math.max(0, Math.min(255, Math.round(rgb.r * gamma))),
|
||||||
|
g: Math.max(0, Math.min(255, Math.round(rgb.g * gamma))),
|
||||||
|
b: Math.max(0, Math.min(255, Math.round(rgb.b * gamma))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RGB object to hex color string
|
||||||
|
* @param {RGB} rgb
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export function toHexColor(rgb) {
|
||||||
|
return (
|
||||||
|
'#' +
|
||||||
|
padStart(rgb.r.toString(16), 2, '0') +
|
||||||
|
padStart(rgb.g.toString(16), 2, '0') +
|
||||||
|
padStart(rgb.b.toString(16), 2, '0')
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue