style: 优化连接弹窗 - 分区折叠 + 视觉层次
- 基本连接信息始终显示, 带卡片背景和图标标题 - TLS/Sentinel/Cluster/SSH 改为可折叠面板, 默认折叠 - 折叠标题显示已启用标签 (Enabled tag) - 内联样式替换为 CSS 类 (.file-input-row, .form-input) - 弹窗宽度 520px -> 580px - 深色/浅色主题适配
This commit is contained in:
@@ -40,9 +40,11 @@ const form = reactive({
|
|||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const testing = ref(false)
|
const testing = ref(false)
|
||||||
const isEdit = ref(false)
|
const isEdit = ref(false)
|
||||||
|
const activeCollapses = ref<string[]>([])
|
||||||
|
|
||||||
watch(() => props.visible, async (v) => {
|
watch(() => props.visible, async (v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
|
activeCollapses.value = []
|
||||||
if (props.connection) {
|
if (props.connection) {
|
||||||
isEdit.value = true
|
isEdit.value = true
|
||||||
const decryptedAuth = await connStore.decryptPassword(props.connection.auth || '')
|
const decryptedAuth = await connStore.decryptPassword(props.connection.auth || '')
|
||||||
@@ -219,53 +221,75 @@ async function handleSave() {
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
:model-value="visible"
|
:model-value="visible"
|
||||||
:title="isEdit ? t('connection.edit') : t('connection.new')"
|
:title="isEdit ? t('connection.edit') : t('connection.new')"
|
||||||
width="520px"
|
width="580px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
@update:model-value="close"
|
@update:model-value="close"
|
||||||
|
class="connection-dialog"
|
||||||
>
|
>
|
||||||
<el-form label-position="top" size="small">
|
<el-form label-position="top" size="small" class="connection-form">
|
||||||
|
<!-- Basic Connection Info -->
|
||||||
|
<div class="section basic-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-icon">🔌</div>
|
||||||
|
<span class="section-title">Basic Connection</span>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
<el-form-item :label="t('connection.name')" required>
|
<el-form-item :label="t('connection.name')" required>
|
||||||
<el-input v-model="form.name" placeholder="My Redis" />
|
<el-input v-model="form.name" placeholder="My Redis" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
<el-form-item :label="t('connection.host')" required>
|
<el-form-item :label="t('connection.host')" required>
|
||||||
<el-input v-model="form.host" placeholder="127.0.0.1" />
|
<el-input v-model="form.host" placeholder="127.0.0.1" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item :label="t('connection.port')">
|
<el-form-item :label="t('connection.port')">
|
||||||
<el-input-number v-model="form.port" :min="1" :max="65535" controls-position="right" style="width:100%" />
|
<el-input-number v-model="form.port" :min="1" :max="65535" controls-position="right" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item :label="t('connection.password')">
|
<el-form-item :label="t('connection.password')">
|
||||||
<el-input v-model="form.auth" type="password" show-password :placeholder="t('connection.password')" />
|
<el-input v-model="form.auth" type="password" show-password :placeholder="t('connection.password')" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('connection.username')">
|
<el-form-item :label="t('connection.username')">
|
||||||
<el-input v-model="form.username" placeholder="default" />
|
<el-input v-model="form.username" placeholder="default" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Key Separator">
|
<el-form-item label="Key Separator">
|
||||||
<el-input v-model="form.separator" placeholder=":" />
|
<el-input v-model="form.separator" placeholder=":" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Advanced Options -->
|
||||||
|
<el-collapse v-model="activeCollapses" class="advanced-collapse">
|
||||||
|
<!-- TLS Section -->
|
||||||
|
<el-collapse-item name="tls">
|
||||||
|
<template #title>
|
||||||
|
<div class="collapse-title">
|
||||||
|
<span class="collapse-icon">🔒</span>
|
||||||
|
<span class="collapse-text">{{ t('connection.tls') }}</span>
|
||||||
|
<el-tag v-if="form.tls" size="small" type="success" effect="dark">Enabled</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="collapse-content">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-checkbox v-model="form.tls">{{ t('connection.tls') }}</el-checkbox>
|
<el-checkbox v-model="form.tls">{{ t('connection.tls') }}</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<template v-if="form.tls">
|
<template v-if="form.tls">
|
||||||
<el-form-item :label="t('connection.tlsCaPath')">
|
<el-form-item :label="t('connection.tlsCaPath')">
|
||||||
<div style="display:flex;gap:8px;">
|
<div class="file-input-row">
|
||||||
<el-input v-model="form.tlsCaPath" placeholder="/path/to/ca.pem" />
|
<el-input v-model="form.tlsCaPath" placeholder="/path/to/ca.pem" />
|
||||||
<el-button @click="selectTlsCaFile">{{ t('connection.selectFile') }}</el-button>
|
<el-button @click="selectTlsCaFile">{{ t('connection.selectFile') }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('connection.tlsCertPath')">
|
<el-form-item :label="t('connection.tlsCertPath')">
|
||||||
<div style="display:flex;gap:8px;">
|
<div class="file-input-row">
|
||||||
<el-input v-model="form.tlsCertPath" placeholder="/path/to/client.pem" />
|
<el-input v-model="form.tlsCertPath" placeholder="/path/to/client.pem" />
|
||||||
<el-button @click="selectTlsCertFile">{{ t('connection.selectFile') }}</el-button>
|
<el-button @click="selectTlsCertFile">{{ t('connection.selectFile') }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('connection.tlsKeyPath')">
|
<el-form-item :label="t('connection.tlsKeyPath')">
|
||||||
<div style="display:flex;gap:8px;">
|
<div class="file-input-row">
|
||||||
<el-input v-model="form.tlsKeyPath" placeholder="/path/to/client-key.pem" />
|
<el-input v-model="form.tlsKeyPath" placeholder="/path/to/client-key.pem" />
|
||||||
<el-button @click="selectTlsKeyFile">{{ t('connection.selectFile') }}</el-button>
|
<el-button @click="selectTlsKeyFile">{{ t('connection.selectFile') }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,9 +298,19 @@ async function handleSave() {
|
|||||||
<el-checkbox v-model="form.tlsRejectUnauthorized">{{ t('connection.tlsRejectUnauthorized') }}</el-checkbox>
|
<el-checkbox v-model="form.tlsRejectUnauthorized">{{ t('connection.tlsRejectUnauthorized') }}</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
<!-- Sentinel -->
|
<!-- Sentinel Section -->
|
||||||
<el-divider />
|
<el-collapse-item name="sentinel">
|
||||||
|
<template #title>
|
||||||
|
<div class="collapse-title">
|
||||||
|
<span class="collapse-icon">🏰</span>
|
||||||
|
<span class="collapse-text">{{ t('connection.sentinel') }}</span>
|
||||||
|
<el-tag v-if="form.sentinelEnabled" size="small" type="success" effect="dark">Enabled</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="collapse-content">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-checkbox v-model="form.sentinelEnabled">{{ t('connection.sentinel') }}</el-checkbox>
|
<el-checkbox v-model="form.sentinelEnabled">{{ t('connection.sentinel') }}</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -289,7 +323,7 @@ async function handleSave() {
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item :label="t('connection.sentinelPort')">
|
<el-form-item :label="t('connection.sentinelPort')">
|
||||||
<el-input-number v-model="form.sentinelPort" :min="1" :max="65535" controls-position="right" style="width:100%" />
|
<el-input-number v-model="form.sentinelPort" :min="1" :max="65535" controls-position="right" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -300,15 +334,35 @@ async function handleSave() {
|
|||||||
<el-input v-model="form.sentinelNodePassword" type="password" show-password />
|
<el-input v-model="form.sentinelNodePassword" type="password" show-password />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
<!-- Cluster -->
|
<!-- Cluster Section -->
|
||||||
<el-divider />
|
<el-collapse-item name="cluster">
|
||||||
|
<template #title>
|
||||||
|
<div class="collapse-title">
|
||||||
|
<span class="collapse-icon">🌐</span>
|
||||||
|
<span class="collapse-text">{{ t('connection.cluster') }}</span>
|
||||||
|
<el-tag v-if="form.cluster" size="small" type="success" effect="dark">Enabled</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="collapse-content">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-checkbox v-model="form.cluster">{{ t('connection.cluster') }}</el-checkbox>
|
<el-checkbox v-model="form.cluster">{{ t('connection.cluster') }}</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
|
||||||
<!-- SSH Tunnel -->
|
<!-- SSH Tunnel Section -->
|
||||||
<el-divider />
|
<el-collapse-item name="ssh">
|
||||||
|
<template #title>
|
||||||
|
<div class="collapse-title">
|
||||||
|
<span class="collapse-icon">🔑</span>
|
||||||
|
<span class="collapse-text">{{ t('connection.ssh') }}</span>
|
||||||
|
<el-tag v-if="form.sshEnabled" size="small" type="success" effect="dark">Enabled</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="collapse-content">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-checkbox v-model="form.sshEnabled">{{ t('connection.ssh') }}</el-checkbox>
|
<el-checkbox v-model="form.sshEnabled">{{ t('connection.ssh') }}</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -321,7 +375,7 @@ async function handleSave() {
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item :label="t('connection.sshPort')">
|
<el-form-item :label="t('connection.sshPort')">
|
||||||
<el-input-number v-model="form.sshPort" :min="1" :max="65535" controls-position="right" style="width:100%" />
|
<el-input-number v-model="form.sshPort" :min="1" :max="65535" controls-position="right" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -332,7 +386,7 @@ async function handleSave() {
|
|||||||
<el-input v-model="form.sshPassword" type="password" show-password :placeholder="t('connection.sshPassword')" />
|
<el-input v-model="form.sshPassword" type="password" show-password :placeholder="t('connection.sshPassword')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('connection.sshPrivateKey')">
|
<el-form-item :label="t('connection.sshPrivateKey')">
|
||||||
<div style="display:flex;gap:8px;">
|
<div class="file-input-row">
|
||||||
<el-input v-model="form.sshPrivateKeyPath" placeholder="~/.ssh/id_rsa" />
|
<el-input v-model="form.sshPrivateKeyPath" placeholder="~/.ssh/id_rsa" />
|
||||||
<el-button @click="selectFile">{{ t('connection.selectFile') }}</el-button>
|
<el-button @click="selectFile">{{ t('connection.selectFile') }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -341,6 +395,9 @@ async function handleSave() {
|
|||||||
<el-input v-model="form.sshPassphrase" type="password" show-password :placeholder="t('connection.sshPassphrase')" />
|
<el-input v-model="form.sshPassphrase" type="password" show-password :placeholder="t('connection.sshPassphrase')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
@@ -357,10 +414,104 @@ async function handleSave() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.connection-form {
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section styling */
|
||||||
|
.section {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-section {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form inputs */
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File input row */
|
||||||
|
.file-input-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-input-row .el-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advanced collapse styling */
|
||||||
|
.advanced-collapse :deep(.el-collapse-item__header) {
|
||||||
|
padding: 12px 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-collapse :deep(.el-collapse-item__wrap) {
|
||||||
|
border: none;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-collapse :deep(.el-collapse-item__content) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-collapse :deep(.el-collapse-item) {
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-text {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-right {
|
.footer-right {
|
||||||
|
|||||||
Reference in New Issue
Block a user