Compare commits
7 Commits
54864fe7d5
...
faad24d1d5
Author | SHA1 | Date |
---|---|---|
tiny-craft | faad24d1d5 | |
tiny-craft | 6fb0eadfbd | |
tiny-craft | 124627d724 | |
tiny-craft | b6eebda177 | |
tiny-craft | 33051f4e46 | |
tiny-craft | 85ab6e4377 | |
tiny-craft | 297286f150 |
|
@ -18,7 +18,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { darkTheme } from 'naive-ui'
|
import { darkTheme } from 'naive-ui'
|
||||||
import KeyFilterDialog from './components/dialogs/KeyFilterDialog.vue'
|
import KeyFilterDialog from './components/dialogs/KeyFilterDialog.vue'
|
||||||
import { WindowSetDarkTheme, WindowSetLightTheme } from 'wailsjs/runtime/runtime.js'
|
import { WindowSetDarkTheme, WindowSetLightTheme } from 'wailsjs/runtime/runtime.js'
|
||||||
import { themeOverrides } from '@/utils/theme.js'
|
import { darkThemeOverrides, themeOverrides } from '@/utils/theme.js'
|
||||||
import AboutDialog from '@/components/dialogs/AboutDialog.vue'
|
import AboutDialog from '@/components/dialogs/AboutDialog.vue'
|
||||||
|
|
||||||
hljs.registerLanguage('json', json)
|
hljs.registerLanguage('json', json)
|
||||||
|
@ -60,7 +60,7 @@ watch(
|
||||||
:inline-theme-disabled="true"
|
:inline-theme-disabled="true"
|
||||||
:locale="prefStore.themeLocale"
|
:locale="prefStore.themeLocale"
|
||||||
:theme="prefStore.isDark ? darkTheme : undefined"
|
:theme="prefStore.isDark ? darkTheme : undefined"
|
||||||
:theme-overrides="themeOverrides"
|
:theme-overrides="prefStore.isDark ? darkThemeOverrides : themeOverrides"
|
||||||
class="fill-height">
|
class="fill-height">
|
||||||
<n-dialog-provider>
|
<n-dialog-provider>
|
||||||
<app-content :loading="initializing" />
|
<app-content :loading="initializing" />
|
||||||
|
|
|
@ -16,6 +16,8 @@ import ToolbarControlWidget from '@/components/common/ToolbarControlWidget.vue'
|
||||||
import { EventsOn, WindowIsFullscreen, WindowIsMaximised, WindowToggleMaximise } from 'wailsjs/runtime/runtime.js'
|
import { EventsOn, WindowIsFullscreen, WindowIsMaximised, WindowToggleMaximise } from 'wailsjs/runtime/runtime.js'
|
||||||
import { isMacOS } from '@/utils/platform.js'
|
import { isMacOS } from '@/utils/platform.js'
|
||||||
import iconUrl from '@/assets/images/icon.png'
|
import iconUrl from '@/assets/images/icon.png'
|
||||||
|
import ResizeableWrapper from '@/components/common/ResizeableWrapper.vue'
|
||||||
|
import { extraTheme } from '@/utils/extra_theme.js'
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
|
||||||
|
@ -25,8 +27,6 @@ const props = defineProps({
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
navMenuWidth: 60,
|
navMenuWidth: 60,
|
||||||
hoverResize: false,
|
|
||||||
resizing: false,
|
|
||||||
toolbarHeight: 45,
|
toolbarHeight: 45,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -34,37 +34,17 @@ const tabStore = useTabStore()
|
||||||
const prefStore = usePreferencesStore()
|
const prefStore = usePreferencesStore()
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
const logPaneRef = ref(null)
|
const logPaneRef = ref(null)
|
||||||
|
const exThemeVars = computed(() => {
|
||||||
|
return extraTheme(prefStore.isDark)
|
||||||
|
})
|
||||||
// const preferences = ref({})
|
// const preferences = ref({})
|
||||||
// provide('preferences', preferences)
|
// provide('preferences', preferences)
|
||||||
|
|
||||||
const saveSidebarWidth = debounce(prefStore.savePreferences, 1000, { trailing: true })
|
const saveSidebarWidth = debounce(prefStore.savePreferences, 1000, { trailing: true })
|
||||||
const handleResize = (evt) => {
|
const handleResize = () => {
|
||||||
if (data.resizing) {
|
|
||||||
prefStore.setAsideWidth(Math.max(evt.clientX - data.navMenuWidth, 300))
|
|
||||||
saveSidebarWidth()
|
saveSidebarWidth()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopResize = () => {
|
|
||||||
data.resizing = false
|
|
||||||
document.removeEventListener('mousemove', handleResize)
|
|
||||||
document.removeEventListener('mouseup', stopResize)
|
|
||||||
}
|
|
||||||
|
|
||||||
const startResize = () => {
|
|
||||||
data.resizing = true
|
|
||||||
document.addEventListener('mousemove', handleResize)
|
|
||||||
document.addEventListener('mouseup', stopResize)
|
|
||||||
}
|
|
||||||
|
|
||||||
const asideWidthVal = computed(() => {
|
|
||||||
return prefStore.behavior.asideWidth + 'px'
|
|
||||||
})
|
|
||||||
|
|
||||||
const dragging = computed(() => {
|
|
||||||
return data.hoverResize || data.resizing
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => tabStore.nav,
|
() => tabStore.nav,
|
||||||
(nav) => {
|
(nav) => {
|
||||||
|
@ -75,8 +55,11 @@ watch(
|
||||||
)
|
)
|
||||||
|
|
||||||
const border = computed(() => {
|
const border = computed(() => {
|
||||||
const color = isMacOS() && false ? '#0000' : themeVars.value.borderColor
|
return isMacOS() ? 'none' : `1px solid ${themeVars.value.borderColor}`
|
||||||
return `1px solid ${color}`
|
})
|
||||||
|
|
||||||
|
const logoWrapperWidth = computed(() => {
|
||||||
|
return `${data.navMenuWidth + prefStore.behavior.asideWidth - 4}px`
|
||||||
})
|
})
|
||||||
|
|
||||||
const borderRadius = ref(10)
|
const borderRadius = ref(10)
|
||||||
|
@ -128,15 +111,9 @@ onMounted(async () => {
|
||||||
<!-- app content-->
|
<!-- app content-->
|
||||||
<n-spin
|
<n-spin
|
||||||
:show="props.loading"
|
:show="props.loading"
|
||||||
:style="{ backgroundColor: themeVars.bodyColor, borderRadius: `${borderRadius}px`, border }"
|
:style="{ borderRadius: `${borderRadius}px`, border }"
|
||||||
:theme-overrides="{ opacitySpinning: 0 }">
|
:theme-overrides="{ opacitySpinning: 0 }">
|
||||||
<div
|
<div id="app-content-wrapper" :style="{ color: themeVars.textColorBase }" class="flex-box-v">
|
||||||
id="app-content-wrapper"
|
|
||||||
:style="{
|
|
||||||
backgroundColor: themeVars.bodyColor,
|
|
||||||
color: themeVars.textColorBase,
|
|
||||||
}"
|
|
||||||
class="flex-box-v">
|
|
||||||
<!-- title bar -->
|
<!-- title bar -->
|
||||||
<div
|
<div
|
||||||
id="app-toolbar"
|
id="app-toolbar"
|
||||||
|
@ -148,7 +125,8 @@ onMounted(async () => {
|
||||||
<div
|
<div
|
||||||
id="app-toolbar-title"
|
id="app-toolbar-title"
|
||||||
:style="{
|
:style="{
|
||||||
width: `${data.navMenuWidth + prefStore.behavior.asideWidth - 4}px`,
|
width: logoWrapperWidth,
|
||||||
|
minWidth: logoWrapperWidth,
|
||||||
paddingLeft: `${logoPaddingLeft}px`,
|
paddingLeft: `${logoPaddingLeft}px`,
|
||||||
}">
|
}">
|
||||||
<n-space :size="3" :wrap="false" :wrap-item="false" align="center">
|
<n-space :size="3" :wrap="false" :wrap-item="false" align="center">
|
||||||
|
@ -161,15 +139,6 @@ onMounted(async () => {
|
||||||
</transition>
|
</transition>
|
||||||
</n-space>
|
</n-space>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
'resize-divider-hover': data.hoverResize,
|
|
||||||
'resize-divider-drag': data.resizing,
|
|
||||||
}"
|
|
||||||
class="resize-divider resize-divider-hide"
|
|
||||||
@mousedown="startResize"
|
|
||||||
@mouseout="data.hoverResize = false"
|
|
||||||
@mouseover="data.hoverResize = true" />
|
|
||||||
<!-- browser tabs -->
|
<!-- browser tabs -->
|
||||||
<div v-show="tabStore.nav === 'browser'" class="app-toolbar-tab flex-item-expand">
|
<div v-show="tabStore.nav === 'browser'" class="app-toolbar-tab flex-item-expand">
|
||||||
<content-value-tab />
|
<content-value-tab />
|
||||||
|
@ -191,40 +160,37 @@ onMounted(async () => {
|
||||||
style="--wails-draggable: none">
|
style="--wails-draggable: none">
|
||||||
<nav-menu v-model:value="tabStore.nav" :width="data.navMenuWidth" />
|
<nav-menu v-model:value="tabStore.nav" :width="data.navMenuWidth" />
|
||||||
<!-- browser page -->
|
<!-- browser page -->
|
||||||
<div v-show="tabStore.nav === 'browser'" :class="{ dragging }" class="flex-box-h flex-item-expand">
|
<div v-show="tabStore.nav === 'browser'" class="flex-box-h flex-item-expand">
|
||||||
<div id="app-side" :style="{ width: asideWidthVal }" class="flex-box-h flex-item">
|
<resizeable-wrapper
|
||||||
|
v-model:size="prefStore.behavior.asideWidth"
|
||||||
|
:min-size="300"
|
||||||
|
:offset="data.navMenuWidth"
|
||||||
|
class="flex-item"
|
||||||
|
@update:size="handleResize">
|
||||||
<browser-pane
|
<browser-pane
|
||||||
v-for="t in tabStore.tabs"
|
v-for="t in tabStore.tabs"
|
||||||
v-show="get(tabStore.currentTab, 'name') === t.name"
|
v-show="get(tabStore.currentTab, 'name') === t.name"
|
||||||
:key="t.name"
|
:key="t.name"
|
||||||
|
class="app-side flex-item-expand" />
|
||||||
|
</resizeable-wrapper>
|
||||||
|
<content-pane
|
||||||
|
v-for="t in tabStore.tabs"
|
||||||
|
v-show="get(tabStore.currentTab, 'name') === t.name"
|
||||||
|
:key="t.name"
|
||||||
|
:server="t.name"
|
||||||
class="flex-item-expand" />
|
class="flex-item-expand" />
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
'resize-divider-hover': data.hoverResize,
|
|
||||||
'resize-divider-drag': data.resizing,
|
|
||||||
}"
|
|
||||||
class="resize-divider"
|
|
||||||
@mousedown="startResize"
|
|
||||||
@mouseout="data.hoverResize = false"
|
|
||||||
@mouseover="data.hoverResize = true" />
|
|
||||||
</div>
|
|
||||||
<content-pane class="flex-item-expand" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- server list page -->
|
<!-- server list page -->
|
||||||
<div v-show="tabStore.nav === 'server'" :class="{ dragging }" class="flex-box-h flex-item-expand">
|
<div v-show="tabStore.nav === 'server'" class="flex-box-h flex-item-expand">
|
||||||
<div id="app-side" :style="{ width: asideWidthVal }" class="flex-box-h flex-item">
|
<resizeable-wrapper
|
||||||
<connection-pane class="flex-item-expand" />
|
v-model:size="prefStore.behavior.asideWidth"
|
||||||
<div
|
:min-size="300"
|
||||||
:class="{
|
:offset="data.navMenuWidth"
|
||||||
'resize-divider-hover': data.hoverResize,
|
class="flex-item"
|
||||||
'resize-divider-drag': data.resizing,
|
@update:size="handleResize">
|
||||||
}"
|
<connection-pane class="app-side flex-item-expand" />
|
||||||
class="resize-divider"
|
</resizeable-wrapper>
|
||||||
@mousedown="startResize"
|
|
||||||
@mouseout="data.hoverResize = false"
|
|
||||||
@mouseover="data.hoverResize = true" />
|
|
||||||
</div>
|
|
||||||
<content-server-pane class="flex-item-expand" />
|
<content-server-pane class="flex-item-expand" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -239,15 +205,16 @@ onMounted(async () => {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#app-content-wrapper {
|
#app-content-wrapper {
|
||||||
width: calc(100vw - 2px);
|
width: 100vw;
|
||||||
height: calc(100vh - 2px);
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
|
background-color: v-bind('themeVars.bodyColor');
|
||||||
|
|
||||||
#app-toolbar {
|
#app-toolbar {
|
||||||
background-color: v-bind('themeVars.tabColor');
|
background-color: v-bind('exThemeVars.titleColor');
|
||||||
border-bottom: 1px solid v-bind('themeVars.borderColor');
|
border-bottom: 1px solid v-bind('exThemeVars.splitColor');
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
|
@ -268,37 +235,14 @@ onMounted(async () => {
|
||||||
height: calc(100% - 60px);
|
height: calc(100% - 60px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#app-side {
|
.app-side {
|
||||||
//overflow: hidden;
|
//overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: v-bind('themeVars.tabColor');
|
background-color: v-bind('exThemeVars.sidebarColor');
|
||||||
|
border-right: 1px solid v-bind('exThemeVars.splitColor');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.resize-divider {
|
|
||||||
width: 3px;
|
|
||||||
border-right: 1px solid v-bind('themeVars.borderColor');
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-divider-hide {
|
|
||||||
background-color: #0000;
|
|
||||||
border-right-color: #0000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-divider-hover {
|
|
||||||
background-color: v-bind('themeVars.borderColor');
|
|
||||||
border-right-color: v-bind('themeVars.borderColor');
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-divider-drag {
|
|
||||||
background-color: v-bind('themeVars.primaryColor');
|
|
||||||
border-right-color: v-bind('themeVars.primaryColor');
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragging {
|
|
||||||
cursor: col-resize !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from,
|
.fade-enter-from,
|
||||||
.fade-leave-to {
|
.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
|
@ -28,4 +28,4 @@ const handleSelectFile = async () => {
|
||||||
</n-input-group>
|
</n-input-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -14,7 +14,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'currentColor',
|
default: '',
|
||||||
},
|
},
|
||||||
strokeWidth: {
|
strokeWidth: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
|
@ -38,9 +38,10 @@ const hasTooltip = computed(() => {
|
||||||
:focusable="false"
|
:focusable="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:text="!border"
|
:text="!border"
|
||||||
|
:color="props.color"
|
||||||
@click.prevent="emit('click')">
|
@click.prevent="emit('click')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :color="props.color" :size="props.size">
|
<n-icon :color="props.color || 'currentColor'" :size="props.size">
|
||||||
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -54,9 +55,10 @@ const hasTooltip = computed(() => {
|
||||||
:focusable="false"
|
:focusable="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:text="!border"
|
:text="!border"
|
||||||
|
:color="props.color"
|
||||||
@click.prevent="emit('click')">
|
@click.prevent="emit('click')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :color="props.color" :size="props.size">
|
<n-icon :color="props.color || 'currentColor'" :size="props.size">
|
||||||
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
<component :is="props.icon" :stroke-width="props.strokeWidth" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
<script setup>
|
||||||
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizeable component wrapper
|
||||||
|
*/
|
||||||
|
const themeVars = useThemeVars()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
minSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
maxSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
offset: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
borderWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 4,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:size'])
|
||||||
|
|
||||||
|
const resizing = ref(false)
|
||||||
|
const hover = ref(false)
|
||||||
|
|
||||||
|
const handleResize = (evt) => {
|
||||||
|
if (resizing.value) {
|
||||||
|
let size = evt.clientX - props.offset
|
||||||
|
if (size < props.minSize) {
|
||||||
|
size = props.minSize
|
||||||
|
}
|
||||||
|
if (props.maxSize > 0 && size > props.maxSize) {
|
||||||
|
size = props.maxSize
|
||||||
|
}
|
||||||
|
emit('update:size', size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopResize = () => {
|
||||||
|
resizing.value = false
|
||||||
|
document.removeEventListener('mousemove', handleResize)
|
||||||
|
document.removeEventListener('mouseup', stopResize)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startResize = () => {
|
||||||
|
if (props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resizing.value = true
|
||||||
|
document.addEventListener('mousemove', handleResize)
|
||||||
|
document.addEventListener('mouseup', stopResize)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseOver = () => {
|
||||||
|
if (props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hover.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :style="{ width: props.size + 'px' }" class="resize-wrapper flex-box-h">
|
||||||
|
<slot></slot>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'resize-divider-hover': hover,
|
||||||
|
'resize-divider-drag': resizing,
|
||||||
|
dragging: hover || resizing,
|
||||||
|
}"
|
||||||
|
:style="{ width: props.borderWidth + 'px', right: Math.floor(-props.borderWidth / 2) + 'px' }"
|
||||||
|
class="resize-divider"
|
||||||
|
@mousedown="startResize"
|
||||||
|
@mouseout="hover = false"
|
||||||
|
@mouseover="handleMouseOver" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.resize-wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.resize-divider {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
transition: background-color 0.3s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-divider-hide {
|
||||||
|
background-color: #0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-divider-hover {
|
||||||
|
background-color: v-bind('themeVars.borderColor');
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-divider-drag {
|
||||||
|
background-color: v-bind('themeVars.primaryColor');
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragging {
|
||||||
|
cursor: col-resize !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -42,12 +42,12 @@ const handleSwitch = (idx) => {
|
||||||
<template>
|
<template>
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
<n-tooltip
|
<n-tooltip
|
||||||
:show-arrow="false"
|
|
||||||
v-for="(icon, i) in props.icons"
|
v-for="(icon, i) in props.icons"
|
||||||
:key="i"
|
:key="i"
|
||||||
:disabled="!(props.tTooltips && props.tTooltips[i])">
|
:disabled="!(props.tTooltips && props.tTooltips[i])"
|
||||||
|
:show-arrow="false">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button :tertiary="i !== props.value" :focusable="false" :size="props.size" @click="handleSwitch(i)">
|
<n-button :focusable="false" :size="props.size" :tertiary="i !== props.value" @click="handleSwitch(i)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :size="props.iconSize">
|
<n-icon :size="props.iconSize">
|
||||||
<component
|
<component
|
||||||
|
@ -62,4 +62,4 @@ const handleSwitch = (idx) => {
|
||||||
</n-button-group>
|
</n-button-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -73,10 +73,10 @@ defineExpose({
|
||||||
<template>
|
<template>
|
||||||
<n-card
|
<n-card
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
|
:theme-overrides="{ borderRadius: '0px' }"
|
||||||
:title="$t('log.launch_log')"
|
:title="$t('log.launch_log')"
|
||||||
class="content-container flex-box-v"
|
class="content-container flex-box-v"
|
||||||
content-style="display: flex;flex-direction: column; overflow: hidden; backgroundColor: gray"
|
content-style="display: flex;flex-direction: column; overflow: hidden; backgroundColor: gray">
|
||||||
:theme-overrides="{ borderRadius: '0px' }">
|
|
||||||
<n-form :disabled="data.loading" class="flex-item" inline>
|
<n-form :disabled="data.loading" class="flex-item" inline>
|
||||||
<n-form-item :label="$t('log.filter_server')">
|
<n-form-item :label="$t('log.filter_server')">
|
||||||
<n-select
|
<n-select
|
||||||
|
@ -162,6 +162,5 @@ defineExpose({
|
||||||
.content-container {
|
.content-container {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-left: 1px solid v-bind('themeVars.borderColor');
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, onUnmounted, reactive, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { get, isEmpty, keyBy, map, size, toUpper } from 'lodash'
|
import { find, map, toUpper } from 'lodash'
|
||||||
import useTabStore from 'stores/tab.js'
|
import useTabStore from 'stores/tab.js'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
|
||||||
import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue'
|
import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue'
|
||||||
import Status from '@/components/icons/Status.vue'
|
import Status from '@/components/icons/Status.vue'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
@ -24,98 +23,10 @@ const themeVars = useThemeVars()
|
||||||
* @property {boolean} autoLoading loading status for auto refresh
|
* @property {boolean} autoLoading loading status for auto refresh
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
const props = defineProps({
|
||||||
*
|
server: String,
|
||||||
* @type {UnwrapNestedRefs<Object.<string, ServerStatusItem>>}
|
|
||||||
*/
|
|
||||||
const serverStatusTab = reactive({})
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} serverName
|
|
||||||
* @return {UnwrapRef<ServerStatusItem>}
|
|
||||||
*/
|
|
||||||
const getServerInfo = (serverName) => {
|
|
||||||
if (isEmpty(serverName)) {
|
|
||||||
return {
|
|
||||||
name: serverName,
|
|
||||||
info: {},
|
|
||||||
autoRefresh: false,
|
|
||||||
autoLoading: false,
|
|
||||||
loading: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!serverStatusTab.hasOwnProperty(serverName)) {
|
|
||||||
serverStatusTab[serverName] = {
|
|
||||||
name: serverName,
|
|
||||||
info: {},
|
|
||||||
autoRefresh: false,
|
|
||||||
autoLoading: false,
|
|
||||||
loading: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serverStatusTab[serverName]
|
|
||||||
}
|
|
||||||
const serverName = computed(() => {
|
|
||||||
if (tabContent.value != null) {
|
|
||||||
return tabContent.value.name
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {ComputedRef<ServerStatusItem>}
|
|
||||||
*/
|
|
||||||
const currentServer = computed(() => {
|
|
||||||
return getServerInfo(serverName.value)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* refresh server status info
|
|
||||||
* @param {string} serverName
|
|
||||||
* @param {boolean} [force] force refresh will show loading indicator
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
const refreshInfo = async (serverName, force) => {
|
|
||||||
const info = getServerInfo(serverName)
|
|
||||||
if (force) {
|
|
||||||
info.loading = true
|
|
||||||
} else {
|
|
||||||
info.autoLoading = true
|
|
||||||
}
|
|
||||||
if (!isEmpty(serverName) && connectionStore.isConnected(serverName)) {
|
|
||||||
try {
|
|
||||||
info.info = await connectionStore.getServerInfo(serverName)
|
|
||||||
} finally {
|
|
||||||
info.loading = false
|
|
||||||
info.autoLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const refreshAllInfo = async (force) => {
|
|
||||||
for (const serverName in serverStatusTab) {
|
|
||||||
await refreshInfo(serverName, force)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let intervalId
|
|
||||||
onMounted(() => {
|
|
||||||
refreshAllInfo(true)
|
|
||||||
intervalId = setInterval(() => {
|
|
||||||
for (const serverName in serverStatusTab) {
|
|
||||||
if (get(serverStatusTab, [serverName, 'autoRefresh'])) {
|
|
||||||
refreshInfo(serverName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 5000)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(intervalId)
|
|
||||||
})
|
|
||||||
|
|
||||||
const connectionStore = useConnectionStore()
|
|
||||||
const tabStore = useTabStore()
|
const tabStore = useTabStore()
|
||||||
const tab = computed(() =>
|
const tab = computed(() =>
|
||||||
map(tabStore.tabs, (item) => ({
|
map(tabStore.tabs, (item) => ({
|
||||||
|
@ -124,35 +35,10 @@ const tab = computed(() =>
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
|
||||||
() => tabStore.nav,
|
|
||||||
(nav) => {
|
|
||||||
if (nav === 'browser') {
|
|
||||||
refreshInfo(serverName.value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => tabStore.tabList,
|
|
||||||
(tabs) => {
|
|
||||||
if (size(tabs) < size(serverStatusTab)) {
|
|
||||||
const tabMap = keyBy(tabs, 'name')
|
|
||||||
// remove unused server status tab
|
|
||||||
for (const t in serverStatusTab) {
|
|
||||||
if (!tabMap.hasOwnProperty(t)) {
|
|
||||||
delete serverStatusTab[t]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
const tabContent = computed(() => {
|
const tabContent = computed(() => {
|
||||||
const tab = tabStore.currentTab
|
const tab = find(tabStore.tabs, { name: props.server })
|
||||||
if (tab == null) {
|
if (tab == null) {
|
||||||
return null
|
return {}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: tab.name,
|
name: tab.name,
|
||||||
|
@ -168,39 +54,36 @@ const tabContent = computed(() => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const showServerStatus = computed(() => {
|
|
||||||
return tabContent.value == null || isEmpty(tabContent.value.keyPath)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isBlankValue = computed(() => {
|
const isBlankValue = computed(() => {
|
||||||
return tabContent.value.value == null
|
return tabContent.value.value == null
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* reload current selection key
|
|
||||||
* @returns {Promise<null>}
|
|
||||||
*/
|
|
||||||
const onReloadKey = async () => {
|
|
||||||
const tab = tabStore.currentTab
|
|
||||||
if (tab == null || isEmpty(tab.key)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
await connectionStore.loadKeyValue(tab.name, tab.db, tab.key, tab.viewAs)
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedSubTab = computed(() => {
|
const selectedSubTab = computed(() => {
|
||||||
const { subTab = 'status' } = tabStore.currentTab || {}
|
const { subTab = 'status' } = tabStore.currentTab || {}
|
||||||
return subTab
|
return subTab
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSwitchSubTab = (name) => {
|
// BUG: naive-ui tabs will set the bottom line to '0px' after switch to another page and back again
|
||||||
tabStore.switchSubTab(name)
|
// watch parent tabs' changing and call 'syncBarPosition' manually
|
||||||
}
|
const tabsRef = ref(null)
|
||||||
|
const cliRef = ref(null)
|
||||||
|
watch(
|
||||||
|
() => tabContent.value?.name,
|
||||||
|
(name) => {
|
||||||
|
if (name === props.server) {
|
||||||
|
nextTick().then(() => {
|
||||||
|
tabsRef.value?.syncBarPosition()
|
||||||
|
cliRef.value?.resizeTerm()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="content-container flex-box-v">
|
<div class="content-container flex-box-v">
|
||||||
<n-tabs
|
<n-tabs
|
||||||
|
ref="tabsRef"
|
||||||
:tabs-padding="5"
|
:tabs-padding="5"
|
||||||
:theme-overrides="{
|
:theme-overrides="{
|
||||||
tabFontWeightActive: 'normal',
|
tabFontWeightActive: 'normal',
|
||||||
|
@ -215,9 +98,9 @@ const onSwitchSubTab = (name) => {
|
||||||
placement="top"
|
placement="top"
|
||||||
tab-style="padding-left: 10px; padding-right: 10px;"
|
tab-style="padding-left: 10px; padding-right: 10px;"
|
||||||
type="line"
|
type="line"
|
||||||
@update:value="onSwitchSubTab">
|
@update:value="tabStore.switchSubTab">
|
||||||
<!-- server status pane -->
|
<!-- server status pane -->
|
||||||
<n-tab-pane :name="BrowserTabType.Status.toString()">
|
<n-tab-pane :name="BrowserTabType.Status.toString()" display-directive="show:lazy">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
<n-icon size="16">
|
<n-icon size="16">
|
||||||
|
@ -226,17 +109,11 @@ const onSwitchSubTab = (name) => {
|
||||||
<span>{{ $t('interface.sub_tab.status') }}</span>
|
<span>{{ $t('interface.sub_tab.status') }}</span>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
<content-server-status
|
<content-server-status :server="props.server" />
|
||||||
v-model:auto-refresh="currentServer.autoRefresh"
|
|
||||||
:auto-loading="currentServer.autoLoading"
|
|
||||||
:info="currentServer.info"
|
|
||||||
:loading="currentServer.loading"
|
|
||||||
:server="currentServer.name"
|
|
||||||
@refresh="refreshInfo(currentServer.name, true)" />
|
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- key detail pane -->
|
<!-- key detail pane -->
|
||||||
<n-tab-pane :name="BrowserTabType.KeyDetail.toString()">
|
<n-tab-pane :name="BrowserTabType.KeyDetail.toString()" display-directive="show:lazy">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
<n-icon size="16">
|
<n-icon size="16">
|
||||||
|
@ -249,20 +126,19 @@ const onSwitchSubTab = (name) => {
|
||||||
</template>
|
</template>
|
||||||
<content-value-wrapper
|
<content-value-wrapper
|
||||||
:blank="isBlankValue"
|
:blank="isBlankValue"
|
||||||
:type="tabContent.type"
|
|
||||||
:db="tabContent.db"
|
:db="tabContent.db"
|
||||||
:key-code="tabContent.keyCode"
|
:key-code="tabContent.keyCode"
|
||||||
:key-path="tabContent.keyPath"
|
:key-path="tabContent.keyPath"
|
||||||
:name="tabContent.name"
|
:name="tabContent.name"
|
||||||
:size="tabContent.size"
|
:size="tabContent.size"
|
||||||
:ttl="tabContent.ttl"
|
:ttl="tabContent.ttl"
|
||||||
|
:type="tabContent.type"
|
||||||
:value="tabContent.value"
|
:value="tabContent.value"
|
||||||
:view-as="tabContent.viewAs"
|
:view-as="tabContent.viewAs" />
|
||||||
@reload="onReloadKey" />
|
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- cli pane -->
|
<!-- cli pane -->
|
||||||
<n-tab-pane :name="BrowserTabType.Cli.toString()">
|
<n-tab-pane :name="BrowserTabType.Cli.toString()" display-directive="show:lazy">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
<n-icon size="16">
|
<n-icon size="16">
|
||||||
|
@ -271,7 +147,7 @@ const onSwitchSubTab = (name) => {
|
||||||
<span>{{ $t('interface.sub_tab.cli') }}</span>
|
<span>{{ $t('interface.sub_tab.cli') }}</span>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
<content-cli :name="currentServer.name" />
|
<content-cli ref="cliRef" :name="props.server" />
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- slow log pane -->
|
<!-- slow log pane -->
|
||||||
|
@ -302,14 +178,18 @@ const onSwitchSubTab = (name) => {
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.content-sub-tab {
|
.content-sub-tab {
|
||||||
background-color: v-bind('themeVars.bodyColor');
|
background-color: v-bind('themeVars.tabColor');
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-sub-tab-pane {
|
.content-sub-tab-pane {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: v-bind('themeVars.tabColor');
|
background-color: v-bind('themeVars.bodyColor');
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.n-tabs .n-tabs-bar {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,33 +6,37 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { get, map } from 'lodash'
|
import { get, map } from 'lodash'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
|
import { extraTheme } from '@/utils/extra_theme.js'
|
||||||
|
import usePreferencesStore from 'stores/preferences.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value content tab on head
|
||||||
|
*/
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const tabStore = useTabStore()
|
const tabStore = useTabStore()
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
|
const prefStore = usePreferencesStore()
|
||||||
|
|
||||||
const onCloseTab = (tabIndex) => {
|
const onCloseTab = (tabIndex) => {
|
||||||
$dialog.warning(i18n.t('dialogue.close_confirm'), () => {
|
|
||||||
const tab = get(tabStore.tabs, tabIndex)
|
const tab = get(tabStore.tabs, tabIndex)
|
||||||
if (tab != null) {
|
if (tab != null) {
|
||||||
|
$dialog.warning(i18n.t('dialogue.close_confirm', { name: tab.name }), () => {
|
||||||
connectionStore.closeConnection(tab.name)
|
connectionStore.closeConnection(tab.name)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeTabStyle = computed(() => {
|
const tabMarkColor = computed(() => {
|
||||||
const { name } = tabStore.currentTab
|
const { name } = tabStore?.currentTab || {}
|
||||||
const { markColor = '' } = connectionStore.serverProfile[name] || {}
|
const { markColor = '' } = connectionStore.serverProfile[name] || {}
|
||||||
return {
|
return markColor
|
||||||
borderTopWidth: markColor ? '3px' : '1px',
|
|
||||||
borderTopColor: markColor || themeVars.value.borderColor,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const tabClass = (idx) => {
|
const tabClass = (idx) => {
|
||||||
if (tabStore.activatedIndex === idx) {
|
if (tabStore.activatedIndex === idx) {
|
||||||
return ['value-tab', 'value-tab-active']
|
return ['value-tab', 'value-tab-active', tabMarkColor.value ? 'value-tab-active_mark' : '']
|
||||||
} else if (tabStore.activatedIndex - 1 === idx) {
|
} else if (tabStore.activatedIndex - 1 === idx) {
|
||||||
return ['value-tab', 'value-tab-inactive']
|
return ['value-tab', 'value-tab-inactive']
|
||||||
} else {
|
} else {
|
||||||
|
@ -46,26 +50,23 @@ const tab = computed(() =>
|
||||||
label: item.title,
|
label: item.title,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const exThemeVars = computed(() => {
|
||||||
|
return extraTheme(prefStore.isDark)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-tabs
|
<n-tabs
|
||||||
v-model:value="tabStore.activatedIndex"
|
v-model:value="tabStore.activatedIndex"
|
||||||
:closable="true"
|
:closable="true"
|
||||||
:tabs-padding="0"
|
:tabs-padding="3"
|
||||||
:tab-style="{
|
|
||||||
borderStyle: 'solid',
|
|
||||||
borderWidth: '1px',
|
|
||||||
borderLeftColor: themeVars.borderColor,
|
|
||||||
borderRightColor: themeVars.borderColor,
|
|
||||||
}"
|
|
||||||
:theme-overrides="{
|
:theme-overrides="{
|
||||||
tabFontWeightActive: 800,
|
tabFontWeightActive: 800,
|
||||||
tabGapSmallCard: 0,
|
tabGapSmallCard: 0,
|
||||||
tabGapMediumCard: 0,
|
tabGapMediumCard: 0,
|
||||||
tabGapLargeCard: 0,
|
tabGapLargeCard: 0,
|
||||||
tabColor: '#0000',
|
tabColor: '#0000',
|
||||||
// tabBorderColor: themeVars.borderColor,
|
|
||||||
tabBorderColor: '#0000',
|
tabBorderColor: '#0000',
|
||||||
tabTextColorCard: themeVars.closeIconColor,
|
tabTextColorCard: themeVars.closeIconColor,
|
||||||
}"
|
}"
|
||||||
|
@ -73,14 +74,7 @@ const tab = computed(() =>
|
||||||
type="card"
|
type="card"
|
||||||
@close="onCloseTab"
|
@close="onCloseTab"
|
||||||
@update:value="(tabIndex) => tabStore.switchTab(tabIndex)">
|
@update:value="(tabIndex) => tabStore.switchTab(tabIndex)">
|
||||||
<n-tab
|
<n-tab v-for="(t, i) in tab" :key="i" :class="tabClass(i)" :closable="true" :name="i" @dblclick.stop="() => {}">
|
||||||
v-for="(t, i) in tab"
|
|
||||||
:key="i"
|
|
||||||
:closable="true"
|
|
||||||
:name="i"
|
|
||||||
:style="tabStore.activatedIndex === i ? activeTabStyle : undefined"
|
|
||||||
:class="tabClass(i)"
|
|
||||||
@dblclick.stop="() => {}">
|
|
||||||
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
<n-icon size="18">
|
<n-icon size="18">
|
||||||
<server stroke-width="4" />
|
<server stroke-width="4" />
|
||||||
|
@ -95,18 +89,23 @@ const tab = computed(() =>
|
||||||
.value-tab {
|
.value-tab {
|
||||||
--wails-draggable: none;
|
--wails-draggable: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border: 1px solid v-bind('exThemeVars.splitColor') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-tab-active {
|
.value-tab-active {
|
||||||
background-color: v-bind('themeVars.bodyColor') !important;
|
background-color: v-bind('themeVars.tabColor') !important;
|
||||||
border-bottom-color: v-bind('themeVars.bodyColor') !important;
|
border-bottom-color: v-bind('themeVars.tabColor') !important;
|
||||||
|
|
||||||
|
&_mark {
|
||||||
|
border-top: 3px solid v-bind('tabMarkColor') !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-tab-inactive {
|
.value-tab-inactive {
|
||||||
border-color: #0000 !important;
|
border-color: #0000 !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: v-bind('themeVars.borderColor') !important;
|
background-color: v-bind('exThemeVars.splitColor') !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Terminal } from 'xterm'
|
import { Terminal } from 'xterm'
|
||||||
import { FitAddon } from 'xterm-addon-fit'
|
import { FitAddon } from 'xterm-addon-fit'
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, defineExpose, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import 'xterm/css/xterm.css'
|
import 'xterm/css/xterm.css'
|
||||||
import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
|
import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
|
||||||
import { get, isEmpty, set, size } from 'lodash'
|
import { get, isEmpty, set, size } from 'lodash'
|
||||||
|
@ -54,7 +54,6 @@ const newTerm = () => {
|
||||||
return { term, fitAddon }
|
return { term, fitAddon }
|
||||||
}
|
}
|
||||||
|
|
||||||
let intervalID
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { term, fitAddon } = newTerm()
|
const { term, fitAddon } = newTerm()
|
||||||
termInst = term
|
termInst = term
|
||||||
|
@ -69,16 +68,9 @@ onMounted(() => {
|
||||||
EventsOn(`cmd:output:${props.name}`, receiveTermOutput)
|
EventsOn(`cmd:output:${props.name}`, receiveTermOutput)
|
||||||
fitAddon.fit()
|
fitAddon.fit()
|
||||||
term.focus()
|
term.focus()
|
||||||
|
|
||||||
intervalID = setInterval(() => {
|
|
||||||
if (props.activated) {
|
|
||||||
resizeTerm()
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(intervalID)
|
|
||||||
// window.removeEventListener('resize', resizeTerm)
|
// window.removeEventListener('resize', resizeTerm)
|
||||||
EventsOff(`cmd:output:${props.name}`)
|
EventsOff(`cmd:output:${props.name}`)
|
||||||
termInst.dispose()
|
termInst.dispose()
|
||||||
|
@ -92,6 +84,10 @@ const resizeTerm = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
resizeTerm,
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => prefStore.general.fontSize,
|
() => prefStore.general.fontSize,
|
||||||
(fontSize) => {
|
(fontSize) => {
|
||||||
|
@ -125,7 +121,6 @@ const onTermData = (data) => {
|
||||||
|
|
||||||
case 13: // enter
|
case 13: // enter
|
||||||
// try to process local command first
|
// try to process local command first
|
||||||
console.log('enter con', getCurrentInput())
|
|
||||||
switch (getCurrentInput()) {
|
switch (getCurrentInput()) {
|
||||||
case 'clear':
|
case 'clear':
|
||||||
case 'clr':
|
case 'clr':
|
||||||
|
@ -366,7 +361,7 @@ const receiveTermOutput = (data) => {
|
||||||
<div ref="termRef" class="xterm" />
|
<div ref="termRef" class="xterm" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style lang="scss" scoped>
|
||||||
.xterm {
|
.xterm {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
|
|
@ -1,36 +1,72 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { get, isEmpty, map, mapValues, pickBy, split, sum, toArray, toNumber } from 'lodash'
|
import { get, isEmpty, map, mapValues, pickBy, split, sum, toArray, toNumber } from 'lodash'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import IconButton from '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
import Filter from '@/components/icons/Filter.vue'
|
import Filter from '@/components/icons/Filter.vue'
|
||||||
import Refresh from '@/components/icons/Refresh.vue'
|
import Refresh from '@/components/icons/Refresh.vue'
|
||||||
|
import useConnectionStore from 'stores/connections.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
info: Object,
|
|
||||||
autoRefresh: false,
|
|
||||||
loading: false,
|
|
||||||
autoLoading: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:autoRefresh', 'refresh'])
|
const connectionStore = useConnectionStore()
|
||||||
|
const serverInfo = ref({})
|
||||||
|
const autoRefresh = ref(false)
|
||||||
|
const loading = ref(false) // loading status for refresh
|
||||||
|
const autoLoading = ref(false) // loading status for auto refresh
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refresh server status info
|
||||||
|
* @param {boolean} [force] force refresh will show loading indicator
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const refreshInfo = async (force) => {
|
||||||
|
if (force) {
|
||||||
|
loading.value = true
|
||||||
|
} else {
|
||||||
|
autoLoading.value = true
|
||||||
|
}
|
||||||
|
if (!isEmpty(props.server) && connectionStore.isConnected(props.server)) {
|
||||||
|
try {
|
||||||
|
serverInfo.value = await connectionStore.getServerInfo(props.server)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
autoLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let intervalID
|
||||||
|
onMounted(() => {
|
||||||
|
refreshInfo()
|
||||||
|
intervalID = setInterval(() => {
|
||||||
|
if (autoRefresh.value === true) {
|
||||||
|
refreshInfo()
|
||||||
|
}
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(intervalID)
|
||||||
|
})
|
||||||
|
|
||||||
const scrollRef = ref(null)
|
const scrollRef = ref(null)
|
||||||
const redisVersion = computed(() => {
|
const redisVersion = computed(() => {
|
||||||
return get(props.info, 'Server.redis_version', '')
|
return get(serverInfo.value, 'Server.redis_version', '')
|
||||||
})
|
})
|
||||||
|
|
||||||
const redisMode = computed(() => {
|
const redisMode = computed(() => {
|
||||||
return get(props.info, 'Server.redis_mode', '')
|
return get(serverInfo.value, 'Server.redis_mode', '')
|
||||||
})
|
})
|
||||||
|
|
||||||
const role = computed(() => {
|
const role = computed(() => {
|
||||||
return get(props.info, 'Replication.role', '')
|
return get(serverInfo.value, 'Replication.role', '')
|
||||||
})
|
})
|
||||||
|
|
||||||
const timeUnit = ['common.unit_minute', 'common.unit_hour', 'common.unit_day']
|
const timeUnit = ['common.unit_minute', 'common.unit_hour', 'common.unit_day']
|
||||||
const uptime = computed(() => {
|
const uptime = computed(() => {
|
||||||
let seconds = get(props.info, 'Server.uptime_in_seconds', 0)
|
let seconds = get(serverInfo.value, 'Server.uptime_in_seconds', 0)
|
||||||
seconds /= 60
|
seconds /= 60
|
||||||
if (seconds < 60) {
|
if (seconds < 60) {
|
||||||
// minutes
|
// minutes
|
||||||
|
@ -46,7 +82,7 @@ const uptime = computed(() => {
|
||||||
|
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||||
const usedMemory = computed(() => {
|
const usedMemory = computed(() => {
|
||||||
let size = get(props.info, 'Memory.used_memory', 0)
|
let size = get(serverInfo.value, 'Memory.used_memory', 0)
|
||||||
let unitIndex = 0
|
let unitIndex = 0
|
||||||
|
|
||||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||||
|
@ -59,7 +95,7 @@ const usedMemory = computed(() => {
|
||||||
|
|
||||||
const totalKeys = computed(() => {
|
const totalKeys = computed(() => {
|
||||||
const regex = /^db\d+$/
|
const regex = /^db\d+$/
|
||||||
const result = pickBy(props.info['Keyspace'], (value, key) => {
|
const result = pickBy(serverInfo.value['Keyspace'], (value, key) => {
|
||||||
return regex.test(key)
|
return regex.test(key)
|
||||||
})
|
})
|
||||||
const nums = mapValues(result, (v) => {
|
const nums = mapValues(result, (v) => {
|
||||||
|
@ -75,8 +111,8 @@ const infoFilter = ref('')
|
||||||
<template>
|
<template>
|
||||||
<n-scrollbar ref="scrollRef">
|
<n-scrollbar ref="scrollRef">
|
||||||
<n-back-top :listen-to="scrollRef" />
|
<n-back-top :listen-to="scrollRef" />
|
||||||
<n-space vertical :wrap-item="false" :size="5" style="padding: 5px">
|
<n-space :size="5" :wrap-item="false" style="padding: 5px" vertical>
|
||||||
<n-card>
|
<n-card embedded>
|
||||||
<template #header>
|
<template #header>
|
||||||
<n-space :wrap-item="false" align="center" inline size="small">
|
<n-space :wrap-item="false" align="center" inline size="small">
|
||||||
{{ props.server }}
|
{{ props.server }}
|
||||||
|
@ -103,19 +139,16 @@ const infoFilter = ref('')
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<n-space align="center" inline>
|
<n-space align="center" inline>
|
||||||
{{ $t('status.auto_refresh') }}
|
{{ $t('status.auto_refresh') }}
|
||||||
<n-switch
|
<n-switch v-model:value="autoRefresh" :loading="autoLoading" />
|
||||||
:loading="props.autoLoading"
|
|
||||||
:value="props.autoRefresh"
|
|
||||||
@update:value="(v) => emit('update:autoRefresh', v)" />
|
|
||||||
<n-tooltip>
|
<n-tooltip>
|
||||||
{{ $t('status.refresh') }}
|
{{ $t('status.refresh') }}
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button
|
<n-button
|
||||||
:loading="props.autoLoading"
|
:loading="autoLoading"
|
||||||
circle
|
circle
|
||||||
size="small"
|
size="small"
|
||||||
tertiary
|
tertiary
|
||||||
@click="emit('refresh')">
|
@click="refreshInfo(true)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :component="Refresh" />
|
<n-icon :component="Refresh" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -124,7 +157,7 @@ const infoFilter = ref('')
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
<n-spin :show="props.loading">
|
<n-spin :show="loading">
|
||||||
<n-grid style="min-width: 500px" x-gap="5">
|
<n-grid style="min-width: 500px" x-gap="5">
|
||||||
<n-gi :span="6">
|
<n-gi :span="6">
|
||||||
<n-statistic :label="$t('status.uptime')" :value="uptime[0]">
|
<n-statistic :label="$t('status.uptime')" :value="uptime[0]">
|
||||||
|
@ -134,7 +167,7 @@ const infoFilter = ref('')
|
||||||
<n-gi :span="6">
|
<n-gi :span="6">
|
||||||
<n-statistic
|
<n-statistic
|
||||||
:label="$t('status.connected_clients')"
|
:label="$t('status.connected_clients')"
|
||||||
:value="get(props.info, 'Clients.connected_clients', 0)" />
|
:value="get(serverInfo, 'Clients.connected_clients', 0)" />
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi :span="6">
|
||||||
<n-statistic :value="totalKeys">
|
<n-statistic :value="totalKeys">
|
||||||
|
@ -151,7 +184,7 @@ const infoFilter = ref('')
|
||||||
</n-grid>
|
</n-grid>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card :title="$t('status.all_info')">
|
<n-card :title="$t('status.all_info')" embedded>
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<n-input v-model:value="infoFilter" clearable placeholder="">
|
<n-input v-model:value="infoFilter" clearable placeholder="">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
|
@ -159,9 +192,9 @@ const infoFilter = ref('')
|
||||||
</template>
|
</template>
|
||||||
</n-input>
|
</n-input>
|
||||||
</template>
|
</template>
|
||||||
<n-spin :show="props.loading">
|
<n-spin :show="loading">
|
||||||
<n-tabs default-value="CPU" placement="left" type="line">
|
<n-tabs default-value="CPU" placement="left" type="line">
|
||||||
<n-tab-pane v-for="(v, k) in props.info" :key="k" :disabled="isEmpty(v)" :name="k">
|
<n-tab-pane v-for="(v, k) in serverInfo" :key="k" :disabled="isEmpty(v)" :name="k">
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="[
|
:columns="[
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,8 +8,10 @@ import ContentValueSet from '@/components/content_value/ContentValueSet.vue'
|
||||||
import ContentValueZset from '@/components/content_value/ContentValueZSet.vue'
|
import ContentValueZset from '@/components/content_value/ContentValueZSet.vue'
|
||||||
import ContentValueStream from '@/components/content_value/ContentValueStream.vue'
|
import ContentValueStream from '@/components/content_value/ContentValueStream.vue'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
import useConnectionStore from 'stores/connections.js'
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
const connectionStore = useConnectionStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
blank: Boolean,
|
blank: Boolean,
|
||||||
|
@ -33,8 +35,6 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['reload'])
|
|
||||||
|
|
||||||
const valueComponents = {
|
const valueComponents = {
|
||||||
[redisTypes.STRING]: ContentValueString,
|
[redisTypes.STRING]: ContentValueString,
|
||||||
[redisTypes.HASH]: ContentValueHash,
|
[redisTypes.HASH]: ContentValueHash,
|
||||||
|
@ -43,18 +43,25 @@ const valueComponents = {
|
||||||
[redisTypes.ZSET]: ContentValueZset,
|
[redisTypes.ZSET]: ContentValueZset,
|
||||||
[redisTypes.STREAM]: ContentValueStream,
|
[redisTypes.STREAM]: ContentValueStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reload current selection key
|
||||||
|
* @returns {Promise<null>}
|
||||||
|
*/
|
||||||
|
const onReloadKey = async () => {
|
||||||
|
await connectionStore.loadKeyValue(props.name, props.db, props.key, props.viewAs)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-empty v-if="props.blank" :description="$t('interface.nonexist_tab_content')" class="empty-content">
|
<n-empty v-if="props.blank" :description="$t('interface.nonexist_tab_content')" class="empty-content">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<n-button :focusable="false" @click="emit('reload')">{{ $t('interface.reload') }}</n-button>
|
<n-button :focusable="false" @click="onReloadKey">{{ $t('interface.reload') }}</n-button>
|
||||||
</template>
|
</template>
|
||||||
</n-empty>
|
</n-empty>
|
||||||
|
<keep-alive v-else>
|
||||||
<component
|
<component
|
||||||
class="content-value-wrapper"
|
|
||||||
:is="valueComponents[props.type]"
|
:is="valueComponents[props.type]"
|
||||||
v-else
|
|
||||||
:db="props.db"
|
:db="props.db"
|
||||||
:key-code="props.keyCode"
|
:key-code="props.keyCode"
|
||||||
:key-path="props.keyPath"
|
:key-path="props.keyPath"
|
||||||
|
@ -63,10 +70,7 @@ const valueComponents = {
|
||||||
:ttl="props.ttl"
|
:ttl="props.ttl"
|
||||||
:value="props.value"
|
:value="props.value"
|
||||||
:view-as="props.viewAs" />
|
:view-as="props.viewAs" />
|
||||||
|
</keep-alive>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style lang="scss" scoped></style>
|
||||||
.content-value-wrapper {
|
|
||||||
background-color: v-bind('themeVars.bodyColor');
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -8,11 +8,13 @@ import Close from '@/components/icons/Close.vue'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
||||||
import { KeyViewType } from '@/consts/key_view_type.js'
|
import { KeyViewType } from '@/consts/key_view_type.js'
|
||||||
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog for new or edit connection
|
* Dialog for new or edit connection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const themeVars = useThemeVars()
|
||||||
const dialogStore = useDialog()
|
const dialogStore = useDialog()
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
|
@ -396,10 +398,13 @@ const onClose = () => {
|
||||||
<div
|
<div
|
||||||
v-for="color in predefineColors"
|
v-for="color in predefineColors"
|
||||||
:key="color"
|
:key="color"
|
||||||
:class="{
|
:style="{
|
||||||
'color-preset-item_selected': generalForm.markColor === color,
|
backgroundColor: color,
|
||||||
|
borderColor:
|
||||||
|
generalForm.markColor === color
|
||||||
|
? themeVars.textColorBase
|
||||||
|
: themeVars.borderColor,
|
||||||
}"
|
}"
|
||||||
:style="{ backgroundColor: color }"
|
|
||||||
class="color-preset-item"
|
class="color-preset-item"
|
||||||
@click="generalForm.markColor = color">
|
@click="generalForm.markColor = color">
|
||||||
<n-icon v-if="isEmpty(color)" :component="Close" size="24" />
|
<n-icon v-if="isEmpty(color)" :component="Close" size="24" />
|
||||||
|
@ -591,13 +596,9 @@ const onClose = () => {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
border: white 3px solid;
|
border-width: 3px;
|
||||||
|
border-style: solid;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
&_selected,
|
|
||||||
&:hover {
|
|
||||||
border-color: #cdd0d6;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -16,52 +16,52 @@ const props = defineProps({
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
|
fill="currentColor"
|
||||||
height="8"
|
height="8"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
width="8"
|
width="8"
|
||||||
x="4"
|
x="4"
|
||||||
y="34" />
|
y="34" />
|
||||||
<rect
|
<rect
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
|
fill="currentColor"
|
||||||
height="12"
|
height="12"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
width="32"
|
width="32"
|
||||||
x="8"
|
x="8"
|
||||||
y="6" />
|
y="6" />
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M24 34V18"
|
d="M24 34V18"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M8 34V26H40V34"
|
d="M8 34V26H40V34"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<rect
|
<rect
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
|
fill="currentColor"
|
||||||
height="8"
|
height="8"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
width="8"
|
width="8"
|
||||||
x="36"
|
x="36"
|
||||||
y="34" />
|
y="34" />
|
||||||
<rect
|
<rect
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
|
fill="currentColor"
|
||||||
height="8"
|
height="8"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
width="8"
|
width="8"
|
||||||
|
|
|
@ -16,31 +16,31 @@ const props = defineProps({
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M44.0001 11C44.0001 11 44 36.0623 44 38C44 41.3137 35.0457 44 24 44C12.9543 44 4.00003 41.3137 4.00003 38C4.00003 36.1423 4 11 4 11"
|
d="M44.0001 11C44.0001 11 44 36.0623 44 38C44 41.3137 35.0457 44 24 44C12.9543 44 4.00003 41.3137 4.00003 38C4.00003 36.1423 4 11 4 11"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M44 29C44 32.3137 35.0457 35 24 35C12.9543 35 4 32.3137 4 29"
|
d="M44 29C44 32.3137 35.0457 35 24 35C12.9543 35 4 32.3137 4 29"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M44 20C44 23.3137 35.0457 26 24 26C12.9543 26 4 23.3137 4 20"
|
d="M44 20C44 23.3137 35.0457 26 24 26C12.9543 26 4 23.3137 4 20"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<ellipse
|
<ellipse
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
cx="24"
|
cx="24"
|
||||||
cy="10"
|
cy="10"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
rx="20"
|
rx="20"
|
||||||
ry="6"
|
ry="6"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -14,93 +14,93 @@ const props = defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
x="6"
|
:stroke-width="props.strokeWidth"
|
||||||
y="6"
|
fill="currentColor"
|
||||||
width="36"
|
|
||||||
height="36"
|
height="36"
|
||||||
rx="3"
|
rx="3"
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
stroke-linejoin="round"
|
||||||
stroke-linejoin="round" />
|
width="36"
|
||||||
|
x="6"
|
||||||
|
y="6" />
|
||||||
<rect
|
<rect
|
||||||
x="13"
|
|
||||||
y="13"
|
|
||||||
width="8"
|
|
||||||
height="8"
|
|
||||||
fill="#FFF"
|
|
||||||
stroke="#FFF"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
stroke-linejoin="round" />
|
fill="#FFF"
|
||||||
|
height="8"
|
||||||
|
stroke="#FFF"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
width="8"
|
||||||
|
x="13"
|
||||||
|
y="13" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M27 13L35 13"
|
d="M27 13L35 13"
|
||||||
stroke="#FFF"
|
stroke="#FFF"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M27 20L35 20"
|
d="M27 20L35 20"
|
||||||
stroke="#FFF"
|
stroke="#FFF"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M13 28L35 28"
|
d="M13 28L35 28"
|
||||||
stroke="#FFF"
|
stroke="#FFF"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M13 35H35"
|
d="M13 35H35"
|
||||||
stroke="#FFF"
|
stroke="#FFF"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg v-else viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
x="6"
|
:stroke-width="props.strokeWidth"
|
||||||
y="6"
|
fill="none"
|
||||||
width="36"
|
|
||||||
height="36"
|
height="36"
|
||||||
rx="3"
|
rx="3"
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
stroke-linejoin="round"
|
||||||
stroke-linejoin="round" />
|
width="36"
|
||||||
|
x="6"
|
||||||
|
y="6" />
|
||||||
<rect
|
<rect
|
||||||
x="13"
|
|
||||||
y="13"
|
|
||||||
width="8"
|
|
||||||
height="8"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
stroke-linejoin="round" />
|
fill="none"
|
||||||
|
height="8"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
width="8"
|
||||||
|
x="13"
|
||||||
|
y="13" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M27 13L35 13"
|
d="M27 13L35 13"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M27 20L35 20"
|
d="M27 20L35 20"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M13 28L35 28"
|
d="M13 28L35 28"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M13 35H35"
|
d="M13 35H35"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -8,38 +8,38 @@ const props = defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M9 42C11.2091 42 13 40.2091 13 38C13 35.7909 11.2091 34 9 34C6.79086 34 5 35.7909 5 38C5 40.2091 6.79086 42 9 42Z"
|
d="M9 42C11.2091 42 13 40.2091 13 38C13 35.7909 11.2091 34 9 34C6.79086 34 5 35.7909 5 38C5 40.2091 6.79086 42 9 42Z"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M9 14C11.2091 14 13 12.2092 13 10C13 7.79086 11.2091 6 9 6C6.79086 6 5 7.79086 5 10C5 12.2092 6.79086 14 9 14Z"
|
d="M9 14C11.2091 14 13 12.2092 13 10C13 7.79086 11.2091 6 9 6C6.79086 6 5 7.79086 5 10C5 12.2092 6.79086 14 9 14Z"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M9 28C11.2091 28 13 26.2092 13 24C13 21.7908 11.2091 20 9 20C6.79086 20 5 21.7908 5 24C5 26.2092 6.79086 28 9 28Z"
|
d="M9 28C11.2091 28 13 26.2092 13 24C13 21.7908 11.2091 20 9 20C6.79086 20 5 21.7908 5 24C5 26.2092 6.79086 28 9 28Z"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M21 24H43"
|
d="M21 24H43"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M21 38H43"
|
d="M21 38H43"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M21 10H43"
|
d="M21 10H43"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -12,24 +12,24 @@ const props = defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
x="13"
|
|
||||||
y="10"
|
|
||||||
width="28"
|
|
||||||
height="34"
|
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
|
height="34"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
stroke-linejoin="round"
|
||||||
stroke-width="3"
|
stroke-width="3"
|
||||||
stroke-linejoin="round" />
|
width="28"
|
||||||
|
x="13"
|
||||||
|
y="10" />
|
||||||
<path
|
<path
|
||||||
d="M35 10V4H8C7.44772 4 7 4.44772 7 5V38H13"
|
d="M35 10V4H8C7.44772 4 7 4.44772 7 5V38H13"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="3"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round"
|
||||||
<path d="M21 22H33" stroke="#FFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
|
stroke-width="3" />
|
||||||
<path d="M21 30H33" stroke="#FFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
|
<path d="M21 22H33" stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
|
||||||
|
<path d="M21 30H33" stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
|
|
|
@ -16,10 +16,10 @@ const props = defineProps({
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M41 4H7C5.34315 4 4 5.34315 4 7V41C4 42.6569 5.34315 44 7 44H41C42.6569 44 44 42.6569 44 41V7C44 5.34315 42.6569 4 41 4Z"
|
d="M41 4H7C5.34315 4 4 5.34315 4 7V41C4 42.6569 5.34315 44 7 44H41C42.6569 44 44 42.6569 44 41V7C44 5.34315 42.6569 4 41 4Z"
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path :stroke-width="props.strokeWidth" d="M4 32H44" stroke="#FFF" stroke-linecap="round" />
|
<path :stroke-width="props.strokeWidth" d="M4 32H44" stroke="#FFF" stroke-linecap="round" />
|
||||||
|
@ -36,15 +36,15 @@ const props = defineProps({
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M44 37V27"
|
d="M44 37V27"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
stroke="currentColor"
|
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
d="M4 37V27"
|
d="M4 37V27"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -12,15 +12,15 @@ const props = defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
d="M42 8H6C4.89543 8 4 8.89543 4 10V38C4 39.1046 4.89543 40 6 40H42C43.1046 40 44 39.1046 44 38V10C44 8.89543 43.1046 8 42 8Z"
|
d="M42 8H6C4.89543 8 4 8.89543 4 10V38C4 39.1046 4.89543 40 6 40H42C43.1046 40 44 39.1046 44 38V10C44 8.89543 43.1046 8 42 8Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="3" />
|
stroke-width="3" />
|
||||||
<path d="M24 17V31" stroke="#FFF" :stroke-width="props.strokeWidth" stroke-linecap="round" />
|
<path :stroke-width="props.strokeWidth" d="M24 17V31" stroke="#FFF" stroke-linecap="round" />
|
||||||
<path d="M32 24V31" stroke="#FFF" :stroke-width="props.strokeWidth" stroke-linecap="round" />
|
<path :stroke-width="props.strokeWidth" d="M32 24V31" stroke="#FFF" stroke-linecap="round" />
|
||||||
<path d="M16 22V31" stroke="#FFF" :stroke-width="props.strokeWidth" stroke-linecap="round" />
|
<path :stroke-width="props.strokeWidth" d="M16 22V31" stroke="#FFF" stroke-linecap="round" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
|
|
|
@ -14,51 +14,51 @@ const props = defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg v-if="props.inverse" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
x="4"
|
:stroke-width="props.strokeWidth"
|
||||||
y="8"
|
fill="currentColor"
|
||||||
width="40"
|
|
||||||
height="32"
|
height="32"
|
||||||
rx="2"
|
rx="2"
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
stroke-linejoin="round"
|
||||||
stroke-linejoin="round" />
|
width="40"
|
||||||
|
x="4"
|
||||||
|
y="8" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M12 18L19 24L12 30"
|
d="M12 18L19 24L12 30"
|
||||||
stroke="#FFF"
|
stroke="#FFF"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M23 32H36"
|
d="M23 32H36"
|
||||||
stroke="#FFF"
|
stroke="#FFF"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg v-else viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect
|
<rect
|
||||||
x="4"
|
:stroke-width="props.strokeWidth"
|
||||||
y="8"
|
fill="none"
|
||||||
width="40"
|
|
||||||
height="32"
|
height="32"
|
||||||
rx="2"
|
rx="2"
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
stroke-linejoin="round"
|
||||||
stroke-linejoin="round" />
|
width="40"
|
||||||
|
x="4"
|
||||||
|
y="8" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M12 18L19 24L12 30"
|
d="M12 18L19 24L12 30"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M23 32H36"
|
d="M23 32H36"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -8,47 +8,47 @@ const props = defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M38 20H18V28H38V20Z"
|
d="M38 20H18V28H38V20Z"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M32 6H18V14H32V6Z"
|
d="M32 6H18V14H32V6Z"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M44 34H18V42H44V34Z"
|
d="M44 34H18V42H44V34Z"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M17 10H5"
|
d="M17 10H5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M17 24H5"
|
d="M17 24H5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M17 38H5"
|
d="M17 38H5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
<path
|
<path
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
d="M5 44V4"
|
d="M5 44V4"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
:stroke-width="props.strokeWidth"
|
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round" />
|
stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -12,24 +12,12 @@ import useConnectionStore from 'stores/connections.js'
|
||||||
import { types } from '@/consts/support_redis_type.js'
|
import { types } from '@/consts/support_redis_type.js'
|
||||||
import Search from '@/components/icons/Search.vue'
|
import Search from '@/components/icons/Search.vue'
|
||||||
import Unlink from '@/components/icons/Unlink.vue'
|
import Unlink from '@/components/icons/Unlink.vue'
|
||||||
import Status from '@/components/icons/Status.vue'
|
|
||||||
import SwitchButton from '@/components/common/SwitchButton.vue'
|
|
||||||
import ListView from '@/components/icons/ListView.vue'
|
|
||||||
import TreeView from '@/components/icons/TreeView.vue'
|
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const tabStore = useTabStore()
|
const tabStore = useTabStore()
|
||||||
const currentName = computed(() => get(tabStore.currentTab, 'name', ''))
|
const currentName = computed(() => get(tabStore.currentTab, 'name', ''))
|
||||||
const browserTreeRef = ref(null)
|
const browserTreeRef = ref(null)
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {ComputedRef<{server: string, db: number, key: string}>}
|
|
||||||
*/
|
|
||||||
const currentSelect = computed(() => {
|
|
||||||
const { server, db, key } = tabStore.currentTab || {}
|
|
||||||
return { server, db, key }
|
|
||||||
})
|
|
||||||
|
|
||||||
const onInfo = () => {
|
const onInfo = () => {
|
||||||
browserTreeRef.value?.handleSelectContextMenu('server_info')
|
browserTreeRef.value?.handleSelectContextMenu('server_info')
|
||||||
|
@ -99,7 +87,7 @@ const filterTypeOptions = computed(() => {
|
||||||
<!-- stroke-width="4"-->
|
<!-- stroke-width="4"-->
|
||||||
<!-- unselect-stroke-width="3"-->
|
<!-- unselect-stroke-width="3"-->
|
||||||
<!-- @update:value="onSwitchView" />-->
|
<!-- @update:value="onSwitchView" />-->
|
||||||
<icon-button :icon="Status" size="20" stroke-width="4" t-tooltip="interface.status" @click="onInfo" />
|
<!-- <icon-button :icon="Status" size="20" stroke-width="4" t-tooltip="interface.status" @click="onInfo" />-->
|
||||||
<icon-button :icon="Refresh" size="20" stroke-width="4" t-tooltip="interface.reload" @click="onRefresh" />
|
<icon-button :icon="Refresh" size="20" stroke-width="4" t-tooltip="interface.reload" @click="onRefresh" />
|
||||||
<div class="flex-item-expand" />
|
<div class="flex-item-expand" />
|
||||||
<icon-button
|
<icon-button
|
||||||
|
|
|
@ -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 } from 'naive-ui'
|
import { NIcon, NSpace, NTag, useThemeVars } from 'naive-ui'
|
||||||
import Key from '@/components/icons/Key.vue'
|
import Key from '@/components/icons/Key.vue'
|
||||||
import Binary from '@/components/icons/Binary.vue'
|
import Binary from '@/components/icons/Binary.vue'
|
||||||
import Database from '@/components/icons/Database.vue'
|
import Database from '@/components/icons/Database.vue'
|
||||||
|
@ -31,6 +31,7 @@ const props = defineProps({
|
||||||
keyView: String,
|
keyView: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const themeVars = useThemeVars()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const loadingConnections = ref(false)
|
const loadingConnections = ref(false)
|
||||||
|
@ -532,6 +533,7 @@ const getDatabaseMenu = (opened, loading, end) => {
|
||||||
icon: LoadList,
|
icon: LoadList,
|
||||||
disabled: end === true,
|
disabled: end === true,
|
||||||
loading: loading === true,
|
loading: loading === true,
|
||||||
|
color: loading === true ? themeVars.value.primaryColor : '',
|
||||||
onClick: () => handleSelectContextMenu('db_loadmore'),
|
onClick: () => handleSelectContextMenu('db_loadmore'),
|
||||||
}),
|
}),
|
||||||
h(IconButton, {
|
h(IconButton, {
|
||||||
|
@ -539,6 +541,7 @@ const getDatabaseMenu = (opened, loading, end) => {
|
||||||
icon: LoadAll,
|
icon: LoadAll,
|
||||||
disabled: end === true,
|
disabled: end === true,
|
||||||
loading: loading === true,
|
loading: loading === true,
|
||||||
|
color: loading === true ? themeVars.value.primaryColor : '',
|
||||||
onClick: () => handleSelectContextMenu('db_loadall'),
|
onClick: () => handleSelectContextMenu('db_loadall'),
|
||||||
}),
|
}),
|
||||||
h(IconButton, {
|
h(IconButton, {
|
||||||
|
|
|
@ -530,10 +530,10 @@ const onCancelOpen = () => {
|
||||||
|
|
||||||
<!-- context menu -->
|
<!-- context menu -->
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
|
:keyboard="true"
|
||||||
:options="contextMenuParam.options"
|
:options="contextMenuParam.options"
|
||||||
:render-label="renderContextLabel"
|
:render-label="renderContextLabel"
|
||||||
:show="contextMenuParam.show"
|
:show="contextMenuParam.show"
|
||||||
:keyboard="true"
|
|
||||||
:x="contextMenuParam.x"
|
:x="contextMenuParam.x"
|
||||||
:y="contextMenuParam.y"
|
:y="contextMenuParam.y"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { BrowserOpenURL } from 'wailsjs/runtime/runtime.js'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
import usePreferencesStore from 'stores/preferences.js'
|
import usePreferencesStore from 'stores/preferences.js'
|
||||||
import Record from '@/components/icons/Record.vue'
|
import Record from '@/components/icons/Record.vue'
|
||||||
|
import { extraTheme } from '@/utils/extra_theme.js'
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
|
|
||||||
|
@ -88,14 +89,14 @@ const renderContextLabel = (option) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const preferencesStore = usePreferencesStore()
|
const prefStore = usePreferencesStore()
|
||||||
const onSelectPreferenceMenu = (key) => {
|
const onSelectPreferenceMenu = (key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'preferences':
|
case 'preferences':
|
||||||
dialogStore.openPreferencesDialog()
|
dialogStore.openPreferencesDialog()
|
||||||
break
|
break
|
||||||
case 'update':
|
case 'update':
|
||||||
preferencesStore.checkForUpdate(true)
|
prefStore.checkForUpdate(true)
|
||||||
break
|
break
|
||||||
case 'about':
|
case 'about':
|
||||||
dialogStore.openAboutDialog()
|
dialogStore.openAboutDialog()
|
||||||
|
@ -106,6 +107,10 @@ const onSelectPreferenceMenu = (key) => {
|
||||||
const openGithub = () => {
|
const openGithub = () => {
|
||||||
BrowserOpenURL('https://github.com/tiny-craft/tiny-rdm')
|
BrowserOpenURL('https://github.com/tiny-craft/tiny-rdm')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exThemeVars = computed(() => {
|
||||||
|
return extraTheme(prefStore.isDark)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -139,7 +144,9 @@ const openGithub = () => {
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#app-nav-menu {
|
#app-nav-menu {
|
||||||
//height: 100vh;
|
//height: 100vh;
|
||||||
//border-right: v-bind('themeVars.borderColor') solid 1px;
|
border-right: v-bind('exThemeVars.splitColor') solid 1px;
|
||||||
|
background-color: v-bind('exThemeVars.sidebarColor');
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.nav-menu-item {
|
.nav-menu-item {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
"log": "Log"
|
"log": "Log"
|
||||||
},
|
},
|
||||||
"dialogue": {
|
"dialogue": {
|
||||||
"close_confirm": "Confirm close this tab and connection",
|
"close_confirm": "Confirm close this tab and connection ({name})",
|
||||||
"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...",
|
||||||
"interrupt_connection": "Cancel",
|
"interrupt_connection": "Cancel",
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
"log": "日志"
|
"log": "日志"
|
||||||
},
|
},
|
||||||
"dialogue": {
|
"dialogue": {
|
||||||
"close_confirm": "是否关闭当前连接",
|
"close_confirm": "是否关闭此连接({name})",
|
||||||
"edit_close_confirm": "编辑前需要关闭相关连接,是否继续",
|
"edit_close_confirm": "编辑前需要关闭相关连接,是否继续",
|
||||||
"opening_connection": "正在打开连接...",
|
"opening_connection": "正在打开连接...",
|
||||||
"interrupt_connection": "中断连接",
|
"interrupt_connection": "中断连接",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { lang } from '@/langs/index.js'
|
import { lang } from '@/langs/index.js'
|
||||||
import { clone, find, get, isEmpty, map, pick, set, split } from 'lodash'
|
import { cloneDeep, find, get, isEmpty, map, pick, set, split } from 'lodash'
|
||||||
import {
|
import {
|
||||||
CheckForUpdate,
|
CheckForUpdate,
|
||||||
GetFontList,
|
GetFontList,
|
||||||
|
@ -178,7 +178,7 @@ const usePreferencesStore = defineStore('preferences', {
|
||||||
async loadPreferences() {
|
async loadPreferences() {
|
||||||
const { success, data } = await GetPreferences()
|
const { success, data } = await GetPreferences()
|
||||||
if (success) {
|
if (success) {
|
||||||
this.lastPref = clone(data)
|
this.lastPref = cloneDeep(data)
|
||||||
this._applyPreferences(data)
|
this._applyPreferences(data)
|
||||||
i18nGlobal.locale.value = this.currentLanguage
|
i18nGlobal.locale.value = this.currentLanguage
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,7 @@ const useTabStore = defineStore('tab', {
|
||||||
/**
|
/**
|
||||||
* set selected keys of current display browser tree
|
* set selected keys of current display browser tree
|
||||||
* @param {string} server
|
* @param {string} server
|
||||||
* @param {string|string[]} keys
|
* @param {string|string[]} [keys]
|
||||||
*/
|
*/
|
||||||
setSelectedKeys(server, keys = null) {
|
setSelectedKeys(server, keys = null) {
|
||||||
let tab = find(this.tabList, { name: server })
|
let tab = find(this.tabList, { name: server })
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* @typedef ExtraTheme
|
||||||
|
* @property {string} titleColor
|
||||||
|
* @property {string} sidebarColor
|
||||||
|
* @property {string} splitColor
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type ExtraTheme
|
||||||
|
*/
|
||||||
|
export const extraLightTheme = {
|
||||||
|
titleColor: '#F0F0F4',
|
||||||
|
sidebarColor: '#F6F6F6',
|
||||||
|
splitColor: '#E0E0E6',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type ExtraTheme
|
||||||
|
*/
|
||||||
|
export const extraDarkTheme = {
|
||||||
|
titleColor: '#202020',
|
||||||
|
sidebarColor: '#202124',
|
||||||
|
splitColor: '#323138',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {boolean} dark
|
||||||
|
* @return ExtraTheme
|
||||||
|
*/
|
||||||
|
export const extraTheme = (dark) => {
|
||||||
|
return dark ? extraDarkTheme : extraLightTheme
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { padStart, size, startsWith } from 'lodash'
|
||||||
* @property {number} r
|
* @property {number} r
|
||||||
* @property {number} g
|
* @property {number} g
|
||||||
* @property {number} b
|
* @property {number} b
|
||||||
|
* @property {number} [a]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +44,28 @@ export function hexGammaCorrection(rgb, gamma) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mix two colors
|
||||||
|
* @param rgba1
|
||||||
|
* @param rgba2
|
||||||
|
* @param weight
|
||||||
|
* @return {{a: number, r: number, b: number, g: number}}
|
||||||
|
*/
|
||||||
|
export function mixColors(rgba1, rgba2, weight = 0.5) {
|
||||||
|
if (rgba1.a === undefined) {
|
||||||
|
rgba1.a = 255
|
||||||
|
}
|
||||||
|
if (rgba2.a === undefined) {
|
||||||
|
rgba2.a = 255
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
r: Math.floor(rgba1.r * (1 - weight) + rgba2.r * weight),
|
||||||
|
g: Math.floor(rgba1.g * (1 - weight) + rgba2.g * weight),
|
||||||
|
b: Math.floor(rgba1.b * (1 - weight) + rgba2.b * weight),
|
||||||
|
a: Math.floor(rgba1.a * (1 - weight) + rgba2.a * weight),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RGB object to hex color string
|
* RGB object to hex color string
|
||||||
* @param {RGB} rgb
|
* @param {RGB} rgb
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { merge } from 'lodash'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type import('naive-ui').GlobalThemeOverrides
|
* @type import('naive-ui').GlobalThemeOverrides
|
||||||
|
@ -12,6 +14,7 @@ export const themeOverrides = {
|
||||||
borderRadiusSmall: '3px',
|
borderRadiusSmall: '3px',
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
scrollbarWidth: '8px',
|
scrollbarWidth: '8px',
|
||||||
|
tabColor: '#FFFFFF',
|
||||||
},
|
},
|
||||||
Tag: {
|
Tag: {
|
||||||
// borderRadius: '3px'
|
// borderRadius: '3px'
|
||||||
|
@ -39,4 +42,27 @@ export const themeOverrides = {
|
||||||
labelTextColor: 'rgb(113,120,128)',
|
labelTextColor: 'rgb(113,120,128)',
|
||||||
labelFontWeight: '450',
|
labelFontWeight: '450',
|
||||||
},
|
},
|
||||||
|
Radio: {
|
||||||
|
buttonColorActive: '#D13B37',
|
||||||
|
buttonTextColorActive: '#FFF',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type import('naive-ui').GlobalThemeOverrides
|
||||||
|
*/
|
||||||
|
const _darkThemeOverrides = {
|
||||||
|
common: {
|
||||||
|
bodyColor: '#1A1A1A',
|
||||||
|
tabColor: '#18181C',
|
||||||
|
},
|
||||||
|
Tree: {
|
||||||
|
nodeTextColor: '#ceced0',
|
||||||
|
},
|
||||||
|
Card: {
|
||||||
|
colorEmbedded: '#18181C',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const darkThemeOverrides = merge({}, themeOverrides, _darkThemeOverrides)
|
||||||
|
|
4
main.go
4
main.go
|
@ -53,7 +53,7 @@ func main() {
|
||||||
AssetServer: &assetserver.Options{
|
AssetServer: &assetserver.Options{
|
||||||
Assets: assets,
|
Assets: assets,
|
||||||
},
|
},
|
||||||
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 0},
|
BackgroundColour: options.NewRGBA(27, 38, 54, 0),
|
||||||
OnStartup: func(ctx context.Context) {
|
OnStartup: func(ctx context.Context) {
|
||||||
sysSvc.Start(ctx)
|
sysSvc.Start(ctx)
|
||||||
connSvc.Start(ctx)
|
connSvc.Start(ctx)
|
||||||
|
@ -80,7 +80,7 @@ func main() {
|
||||||
Icon: icon,
|
Icon: icon,
|
||||||
},
|
},
|
||||||
WebviewIsTransparent: false,
|
WebviewIsTransparent: false,
|
||||||
WindowIsTranslucent: false,
|
WindowIsTranslucent: true,
|
||||||
},
|
},
|
||||||
Windows: &windows.Options{
|
Windows: &windows.Options{
|
||||||
WebviewIsTransparent: true,
|
WebviewIsTransparent: true,
|
||||||
|
|
Loading…
Reference in New Issue