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 testing = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const activeCollapses = ref<string[]>([])
|
||||
|
||||
watch(() => props.visible, async (v) => {
|
||||
if (v) {
|
||||
activeCollapses.value = []
|
||||
if (props.connection) {
|
||||
isEdit.value = true
|
||||
const decryptedAuth = await connStore.decryptPassword(props.connection.auth || '')
|
||||
@@ -219,128 +221,183 @@ async function handleSave() {
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="isEdit ? t('connection.edit') : t('connection.new')"
|
||||
width="520px"
|
||||
width="580px"
|
||||
:close-on-click-modal="false"
|
||||
@update:model-value="close"
|
||||
class="connection-dialog"
|
||||
>
|
||||
<el-form label-position="top" size="small">
|
||||
<el-form-item :label="t('connection.name')" required>
|
||||
<el-input v-model="form.name" placeholder="My Redis" />
|
||||
</el-form-item>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="t('connection.host')" required>
|
||||
<el-input v-model="form.host" placeholder="127.0.0.1" />
|
||||
<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-input v-model="form.name" placeholder="My Redis" class="form-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<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-row :gutter="12">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="t('connection.host')" required>
|
||||
<el-input v-model="form.host" placeholder="127.0.0.1" class="form-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="t('connection.port')">
|
||||
<el-input-number v-model="form.port" :min="1" :max="65535" controls-position="right" class="form-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="t('connection.password')">
|
||||
<el-input v-model="form.auth" type="password" show-password :placeholder="t('connection.password')" class="form-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="t('connection.password')">
|
||||
<el-input v-model="form.auth" type="password" show-password :placeholder="t('connection.password')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.username')">
|
||||
<el-input v-model="form.username" placeholder="default" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Key Separator">
|
||||
<el-input v-model="form.separator" placeholder=":" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.tls">{{ t('connection.tls') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.tls">
|
||||
<el-form-item :label="t('connection.tlsCaPath')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.tlsCaPath" placeholder="/path/to/ca.pem" />
|
||||
<el-button @click="selectTlsCaFile">{{ t('connection.selectFile') }}</el-button>
|
||||
<el-form-item :label="t('connection.username')">
|
||||
<el-input v-model="form.username" placeholder="default" class="form-input" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Key Separator">
|
||||
<el-input v-model="form.separator" placeholder=":" class="form-input" />
|
||||
</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-checkbox v-model="form.tls">{{ t('connection.tls') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.tls">
|
||||
<el-form-item :label="t('connection.tlsCaPath')">
|
||||
<div class="file-input-row">
|
||||
<el-input v-model="form.tlsCaPath" placeholder="/path/to/ca.pem" />
|
||||
<el-button @click="selectTlsCaFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.tlsCertPath')">
|
||||
<div class="file-input-row">
|
||||
<el-input v-model="form.tlsCertPath" placeholder="/path/to/client.pem" />
|
||||
<el-button @click="selectTlsCertFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.tlsKeyPath')">
|
||||
<div class="file-input-row">
|
||||
<el-input v-model="form.tlsKeyPath" placeholder="/path/to/client-key.pem" />
|
||||
<el-button @click="selectTlsKeyFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.tlsRejectUnauthorized">{{ t('connection.tlsRejectUnauthorized') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.tlsCertPath')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.tlsCertPath" placeholder="/path/to/client.pem" />
|
||||
<el-button @click="selectTlsCertFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.tlsKeyPath')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.tlsKeyPath" placeholder="/path/to/client-key.pem" />
|
||||
<el-button @click="selectTlsKeyFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.tlsRejectUnauthorized">{{ t('connection.tlsRejectUnauthorized') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
|
||||
<!-- Sentinel -->
|
||||
<el-divider />
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.sentinelEnabled">{{ t('connection.sentinel') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.sentinelEnabled">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="t('connection.sentinelHost')">
|
||||
<el-input v-model="form.sentinelHost" placeholder="192.168.1.1" />
|
||||
<!-- Sentinel Section -->
|
||||
<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-checkbox v-model="form.sentinelEnabled">{{ t('connection.sentinel') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<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-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="t('connection.sentinelMasterName')">
|
||||
<el-input v-model="form.sentinelMasterName" placeholder="mymaster" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sentinelNodePassword')">
|
||||
<el-input v-model="form.sentinelNodePassword" type="password" show-password />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- Cluster -->
|
||||
<el-divider />
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.cluster">{{ t('connection.cluster') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<!-- SSH Tunnel -->
|
||||
<el-divider />
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.sshEnabled">{{ t('connection.ssh') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.sshEnabled">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="t('connection.sshHost')">
|
||||
<el-input v-model="form.sshHost" placeholder="192.168.1.1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<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-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="t('connection.sshUsername')">
|
||||
<el-input v-model="form.sshUsername" placeholder="root" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sshPassword')">
|
||||
<el-input v-model="form.sshPassword" type="password" show-password :placeholder="t('connection.sshPassword')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sshPrivateKey')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.sshPrivateKeyPath" placeholder="~/.ssh/id_rsa" />
|
||||
<el-button @click="selectFile">{{ t('connection.selectFile') }}</el-button>
|
||||
<template v-if="form.sentinelEnabled">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="t('connection.sentinelHost')">
|
||||
<el-input v-model="form.sentinelHost" placeholder="192.168.1.1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="t('connection.sentinelPort')">
|
||||
<el-input-number v-model="form.sentinelPort" :min="1" :max="65535" controls-position="right" class="form-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="t('connection.sentinelMasterName')">
|
||||
<el-input v-model="form.sentinelMasterName" placeholder="mymaster" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sentinelNodePassword')">
|
||||
<el-input v-model="form.sentinelNodePassword" type="password" show-password />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sshPassphrase')">
|
||||
<el-input v-model="form.sshPassphrase" type="password" show-password :placeholder="t('connection.sshPassphrase')" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
|
||||
<!-- Cluster Section -->
|
||||
<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-checkbox v-model="form.cluster">{{ t('connection.cluster') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
|
||||
<!-- SSH Tunnel Section -->
|
||||
<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-checkbox v-model="form.sshEnabled">{{ t('connection.ssh') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.sshEnabled">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="t('connection.sshHost')">
|
||||
<el-input v-model="form.sshHost" placeholder="192.168.1.1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="t('connection.sshPort')">
|
||||
<el-input-number v-model="form.sshPort" :min="1" :max="65535" controls-position="right" class="form-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="t('connection.sshUsername')">
|
||||
<el-input v-model="form.sshUsername" placeholder="root" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sshPassword')">
|
||||
<el-input v-model="form.sshPassword" type="password" show-password :placeholder="t('connection.sshPassword')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sshPrivateKey')">
|
||||
<div class="file-input-row">
|
||||
<el-input v-model="form.sshPrivateKeyPath" placeholder="~/.ssh/id_rsa" />
|
||||
<el-button @click="selectFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.sshPassphrase')">
|
||||
<el-input v-model="form.sshPassphrase" type="password" show-password :placeholder="t('connection.sshPassphrase')" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -357,10 +414,104 @@ async function handleSave() {
|
||||
</template>
|
||||
|
||||
<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 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer-right {
|
||||
|
||||
Reference in New Issue
Block a user