style: 优化连接弹窗 - 分区折叠 + 视觉层次

- 基本连接信息始终显示, 带卡片背景和图标标题
- TLS/Sentinel/Cluster/SSH 改为可折叠面板, 默认折叠
- 折叠标题显示已启用标签 (Enabled tag)
- 内联样式替换为 CSS 类 (.file-input-row, .form-input)
- 弹窗宽度 520px -> 580px
- 深色/浅色主题适配
This commit is contained in:
2026-07-11 14:55:39 +08:00
parent 6be62e0fe6
commit f6b85106a2
@@ -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 {