Update AddConnectionForm.vue
This commit is contained in:
@@ -194,88 +194,98 @@ const handleSubmit = async () => {
|
|||||||
<div class="add-connection-form-overlay">
|
<div class="add-connection-form-overlay">
|
||||||
<div class="add-connection-form">
|
<div class="add-connection-form">
|
||||||
<h3>{{ formTitle }}</h3> <!-- 使用计算属性 -->
|
<h3>{{ formTitle }}</h3> <!-- 使用计算属性 -->
|
||||||
<form @submit.prevent="handleSubmit"> <!-- 移除 form-content class -->
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="form-fields two-columns"> <!-- 添加 two-columns class -->
|
<div class="form-sections"> <!-- Container for sections -->
|
||||||
<!-- Column 1: Basic Info -->
|
|
||||||
<div class="form-column">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="conn-name">{{ t('connections.form.name') }} ({{ t('connections.form.optional') }})</label>
|
|
||||||
<input type="text" id="conn-name" v-model="formData.name" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="conn-host">{{ t('connections.form.host') }}</label>
|
|
||||||
<input type="text" id="conn-host" v-model="formData.host" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="conn-port">{{ t('connections.form.port') }}</label>
|
|
||||||
<input type="number" id="conn-port" v-model.number="formData.port" required min="1" max="65535" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="conn-username">{{ t('connections.form.username') }}</label>
|
|
||||||
<input type="text" id="conn-username" v-model="formData.username" required />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Column 2: Authentication, Proxy, Tags -->
|
<fieldset class="form-section">
|
||||||
<div class="form-column">
|
<legend>{{ t('connections.form.sectionBasic', '基本信息') }}</legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="conn-auth-method">{{ t('connections.form.authMethod') }}</label>
|
<label for="conn-name">{{ t('connections.form.name') }} ({{ t('connections.form.optional') }})</label>
|
||||||
<select id="conn-auth-method" v-model="formData.auth_method">
|
<input type="text" id="conn-name" v-model="formData.name" />
|
||||||
<option value="password">{{ t('connections.form.authMethodPassword') }}</option>
|
</div>
|
||||||
<option value="key">{{ t('connections.form.authMethodKey') }}</option>
|
<!-- Host and Port Row -->
|
||||||
</select>
|
<div class="form-row">
|
||||||
</div>
|
<div class="form-group form-group-host">
|
||||||
|
<label for="conn-host">{{ t('connections.form.host') }}</label>
|
||||||
|
<input type="text" id="conn-host" v-model="formData.host" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-group-port">
|
||||||
|
<label for="conn-port">{{ t('connections.form.port') }}</label>
|
||||||
|
<input type="number" id="conn-port" v-model.number="formData.port" required min="1" max="65535" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group" v-if="formData.auth_method === 'password'">
|
<fieldset class="form-section">
|
||||||
<label for="conn-password">{{ t('connections.form.password') }}</label>
|
<legend>{{ t('connections.form.sectionAuth', '认证方式') }}</legend>
|
||||||
<input type="password" id="conn-password" v-model="formData.password" :required="formData.auth_method === 'password' && !isEditMode" />
|
<!-- Username moved here -->
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="formData.auth_method === 'key'">
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="conn-private-key">{{ t('connections.form.privateKey') }}</label>
|
<label for="conn-username">{{ t('connections.form.username') }}</label>
|
||||||
<textarea id="conn-private-key" v-model="formData.private_key" rows="4" :required="formData.auth_method === 'key' && !isEditMode"></textarea>
|
<input type="text" id="conn-username" v-model="formData.username" required />
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Auth Method -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="conn-passphrase">{{ t('connections.form.passphrase') }} ({{ t('connections.form.optional') }})</label>
|
<label for="conn-auth-method">{{ t('connections.form.authMethod') }}</label>
|
||||||
<input type="password" id="conn-passphrase" v-model="formData.passphrase" />
|
<select id="conn-auth-method" v-model="formData.auth_method">
|
||||||
</div>
|
<option value="password">{{ t('connections.form.authMethodPassword') }}</option>
|
||||||
<div class="form-group" v-if="isEditMode && formData.auth_method === 'key'">
|
<option value="key">{{ t('connections.form.authMethodKey') }}</option>
|
||||||
<small>{{ t('connections.form.keyUpdateNote') }}</small>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" v-if="formData.auth_method === 'password'">
|
||||||
<label for="conn-proxy">{{ t('connections.form.proxy') }} ({{ t('connections.form.optional') }})</label>
|
<label for="conn-password">{{ t('connections.form.password') }}</label>
|
||||||
<select id="conn-proxy" v-model="formData.proxy_id">
|
<input type="password" id="conn-password" v-model="formData.password" :required="formData.auth_method === 'password' && !isEditMode" />
|
||||||
<option :value="null">{{ t('connections.form.noProxy') }}</option>
|
</div>
|
||||||
<option v-for="proxy in proxies" :key="proxy.id" :value="proxy.id">
|
|
||||||
{{ proxy.name }} ({{ proxy.type }} - {{ proxy.host }}:{{ proxy.port }})
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<div v-if="isProxyLoading" class="loading-small">{{ t('proxies.loading') }}</div>
|
|
||||||
<div v-if="proxyStoreError" class="error-small">{{ t('proxies.error', { error: proxyStoreError }) }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div v-if="formData.auth_method === 'key'">
|
||||||
<label>{{ t('connections.form.tags') }} ({{ t('connections.form.optional') }})</label>
|
<div class="form-group">
|
||||||
<TagInput v-model="formData.tag_ids" />
|
<label for="conn-private-key">{{ t('connections.form.privateKey') }}</label>
|
||||||
</div>
|
<textarea id="conn-private-key" v-model="formData.private_key" rows="4" :required="formData.auth_method === 'key' && !isEditMode"></textarea>
|
||||||
</div> <!-- End Column 2 -->
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="conn-passphrase">{{ t('connections.form.passphrase') }} ({{ t('connections.form.optional') }})</label>
|
||||||
|
<input type="password" id="conn-passphrase" v-model="formData.passphrase" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group" v-if="isEditMode && formData.auth_method === 'key'">
|
||||||
|
<small>{{ t('connections.form.keyUpdateNote') }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<!-- Error message spans across columns -->
|
<fieldset class="form-section">
|
||||||
<div v-if="formError || storeError" class="error-message full-width-error">
|
<legend>{{ t('connections.form.sectionAdvanced', '高级选项') }}</legend>
|
||||||
{{ formError || storeError }}
|
<div class="form-group">
|
||||||
</div>
|
<label for="conn-proxy">{{ t('connections.form.proxy') }} ({{ t('connections.form.optional') }})</label>
|
||||||
</div> <!-- 结束 form-fields -->
|
<select id="conn-proxy" v-model="formData.proxy_id">
|
||||||
|
<option :value="null">{{ t('connections.form.noProxy') }}</option>
|
||||||
|
<option v-for="proxy in proxies" :key="proxy.id" :value="proxy.id">
|
||||||
|
{{ proxy.name }} ({{ proxy.type }} - {{ proxy.host }}:{{ proxy.port }})
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<div v-if="isProxyLoading" class="loading-small">{{ t('proxies.loading') }}</div>
|
||||||
|
<div v-if="proxyStoreError" class="error-small">{{ t('proxies.error', { error: proxyStoreError }) }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-group">
|
||||||
<button type="submit" :disabled="isLoading">
|
<label>{{ t('connections.form.tags') }} ({{ t('connections.form.optional') }})</label>
|
||||||
{{ submitButtonText }}
|
<TagInput v-model="formData.tag_ids" />
|
||||||
</button>
|
</div>
|
||||||
<button type="button" @click="emit('close')" :disabled="isLoading">{{ t('connections.form.cancel') }}</button>
|
</fieldset>
|
||||||
</div>
|
|
||||||
</form>
|
<!-- Error message remains outside sections -->
|
||||||
|
<div v-if="formError || storeError" class="error-message">
|
||||||
|
{{ formError || storeError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- 结束 form-sections -->
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" :disabled="isLoading">
|
||||||
|
{{ submitButtonText }}
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="emit('close')" :disabled="isLoading">{{ t('connections.form.cancel') }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -300,58 +310,88 @@ const handleSubmit = async () => {
|
|||||||
padding: calc(var(--base-padding, 1rem) * 2);
|
padding: calc(var(--base-padding, 1rem) * 2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25); /* 调整阴影 */
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25); /* 调整阴影 */
|
||||||
min-width: 350px;
|
min-width: 320px; /* 减小最小宽度 */
|
||||||
max-width: 750px; /* 增加最大宽度以容纳两列 */
|
max-width: 600px; /* 调回宽度 */
|
||||||
width: 80vw; /* 宽度适应视口 */
|
width: 90vw;
|
||||||
border: 1px solid var(--border-color, #ccc);
|
border: 1px solid var(--border-color, #ccc);
|
||||||
/* 移除 max-height 和 flex 布局 */
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: calc(var(--base-padding, 1rem) * 1.5); /* 减少整体内边距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: calc(var(--base-margin, 0.5rem) * 4); /* 增加标题下边距 */
|
margin-bottom: calc(var(--base-margin, 0.5rem) * 2); /* 进一步减少标题下边距 */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: var(--text-color, #333);
|
color: var(--text-color, #333);
|
||||||
font-family: var(--font-family-sans-serif, sans-serif);
|
font-family: var(--font-family-sans-serif, sans-serif);
|
||||||
font-size: 1.4em; /* 稍微增大标题字号 */
|
font-size: 1.3em; /* 减小标题字号 */
|
||||||
font-weight: 600; /* 加粗标题 */
|
font-weight: 600;
|
||||||
flex-shrink: 0; /* 防止标题被压缩 */
|
flex-shrink: 0;
|
||||||
padding-bottom: calc(var(--base-padding, 1rem) * 1); /* 增加标题和内容间距 */
|
padding-bottom: calc(var(--base-padding, 1rem) * 0.3); /* 进一步减少标题和内容间距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 移除 .form-content 滚动相关样式 */
|
/* 移除两列布局相关样式 (.two-columns, .form-column, .full-width-error) */
|
||||||
|
|
||||||
/* 两列布局样式 */
|
/* 新增:表单分段样式 */
|
||||||
.form-fields.two-columns {
|
.form-sections {
|
||||||
|
flex-grow: 1; /* 占据主要空间 */
|
||||||
|
overflow-y: auto; /* 使 sections 内部可滚动 */
|
||||||
|
padding: 5px; /* 增加一点内边距防止滚动条遮挡 */
|
||||||
|
margin: -5px; /* 抵消内边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
border: 1px solid var(--border-color, #ddd);
|
||||||
|
border-radius: 5px; /* 稍小圆角 */
|
||||||
|
padding: calc(var(--base-padding, 1rem) * 1); /* 进一步减少 fieldset 内边距 */
|
||||||
|
margin-bottom: calc(var(--base-margin, 0.5rem) * 1.5); /* 进一步减少 fieldset 间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section legend {
|
||||||
|
padding: 0 calc(var(--base-padding, 1rem) * 0.5); /* legend 左右内边距 */
|
||||||
|
font-weight: 600; /* 加粗 legend */
|
||||||
|
color: var(--text-color, #333);
|
||||||
|
font-size: 1.05em; /* 减小 legend 字号 */
|
||||||
|
margin-left: calc(var(--base-margin, 0.5rem) * 0.8); /* 减少 legend 左边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 认证部分内部可以考虑两列,如果需要的话 */
|
||||||
|
/* .auth-section-content { display: flex; gap: 1rem; } */
|
||||||
|
/* .auth-section-content > * { flex: 1; } */
|
||||||
|
|
||||||
|
|
||||||
|
/* Host/Port 行样式 */
|
||||||
|
.form-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap; /* 允许换行,虽然主要目的是两列 */
|
gap: calc(var(--base-padding, 1rem) * 1); /* Host 和 Port 之间的间距 */
|
||||||
gap: calc(var(--base-padding, 1rem) * 2); /* 列之间的间距 */
|
align-items: flex-start; /* 顶部对齐 */
|
||||||
}
|
}
|
||||||
|
.form-row .form-group {
|
||||||
.form-column {
|
margin-bottom: 0; /* 移除行内元素的下边距,由行处理 */
|
||||||
flex: 1; /* 每列占据可用空间 */
|
|
||||||
min-width: 250px; /* 设置最小宽度,防止列过窄 */
|
|
||||||
}
|
}
|
||||||
|
.form-group-host {
|
||||||
/* 错误消息跨列 */
|
flex: 3; /* Host 占据更多空间 */
|
||||||
.full-width-error {
|
}
|
||||||
width: 100%; /* 占据父容器(.two-columns)的全部宽度 */
|
.form-group-port {
|
||||||
order: 99; /* 确保错误消息在列之后显示 */
|
flex: 1; /* Port 占据较少空间 */
|
||||||
margin-top: var(--base-margin, 0.5rem); /* 与上方元素保持间距 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: calc(var(--base-margin, 0.5rem) * 2); /* 保持组间距 */
|
margin-bottom: calc(var(--base-margin, 0.5rem) * 1.2); /* 进一步减少组间距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: calc(var(--base-margin, 0.5rem) * 0.8); /* 调整标签下边距 */
|
margin-bottom: calc(var(--base-margin, 0.5rem) * 0.8); /* 调整标签下边距 */
|
||||||
font-weight: 500; /* 调整标签字重 */
|
font-weight: 500;
|
||||||
font-size: 0.95em; /* 调整标签字号 */
|
font-size: 0.9em; /* 减小标签字号 */
|
||||||
color: var(--text-color-secondary, #666); /* 使用次要文本颜色 */
|
color: var(--text-color-secondary, #666);
|
||||||
font-family: var(--font-family-sans-serif, sans-serif);
|
font-family: var(--font-family-sans-serif, sans-serif);
|
||||||
|
margin-bottom: calc(var(--base-margin, 0.5rem) * 0.5); /* 减少标签下边距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
@@ -360,9 +400,9 @@ input[type="password"],
|
|||||||
select,
|
select,
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: calc(var(--base-padding, 1rem) * 0.6); /* 调整输入框内边距 */
|
padding: calc(var(--base-padding, 1rem) * 0.5); /* 减少输入框内边距 */
|
||||||
border: 1px solid var(--border-color, #ccc);
|
border: 1px solid var(--border-color, #ccc);
|
||||||
border-radius: 4px;
|
border-radius: 3px; /* 稍小圆角 */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: var(--app-bg-color, white);
|
background-color: var(--app-bg-color, white);
|
||||||
color: var(--text-color, #333);
|
color: var(--text-color, #333);
|
||||||
@@ -382,8 +422,8 @@ textarea:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
min-height: 60px; /* 减小文本域最小高度 */
|
min-height: 50px; /* 进一步减小文本域最小高度 */
|
||||||
resize: vertical; /* 允许垂直调整大小 */
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -425,17 +465,24 @@ select {
|
|||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: calc(var(--base-margin, 0.5rem) * 2); /* 减少顶部间距 */
|
margin-top: calc(var(--base-margin, 0.5rem) * 1.5); /* 减少顶部间距 */
|
||||||
padding-top: calc(var(--base-padding, 1rem) * 1); /* 在按钮上方增加间距 */
|
padding-top: calc(var(--base-padding, 1rem) * 0.8); /* 减少按钮上方间距 */
|
||||||
border-top: 1px solid var(--border-color, #eee); /* 添加分隔线 */
|
border-top: 1px solid var(--border-color, #eee);
|
||||||
/* 移除 flex-shrink 和背景色、内外边距调整 */
|
flex-shrink: 0; /* 防止按钮区域压缩 */
|
||||||
|
/* 确保按钮区域背景色,避免滚动内容透视 */
|
||||||
|
background-color: var(--app-bg-color, white);
|
||||||
|
padding-left: calc(var(--base-padding, 1rem) * 0.5);
|
||||||
|
padding-right: calc(var(--base-padding, 1rem) * 0.5);
|
||||||
|
padding-bottom: calc(var(--base-padding, 1rem) * 0.5);
|
||||||
|
margin-left: calc(var(--base-padding, 1rem) * -0.5); /* 抵消容器内边距 */
|
||||||
|
margin-right: calc(var(--base-padding, 1rem) * -0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions button {
|
.form-actions button {
|
||||||
margin-left: var(--base-margin, 0.5rem);
|
margin-left: calc(var(--base-margin, 0.5rem) * 0.8); /* 减少按钮左边距 */
|
||||||
padding: calc(var(--base-padding, 1rem) * 0.7) calc(var(--base-padding, 1rem) * 1.4); /* 调整按钮内边距 */
|
padding: calc(var(--base-padding, 1rem) * 0.5) calc(var(--base-padding, 1rem) * 1.2); /* 减少按钮内边距 */
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
border-radius: 3px; /* 稍小圆角 */
|
||||||
font-family: var(--font-family-sans-serif, sans-serif);
|
font-family: var(--font-family-sans-serif, sans-serif);
|
||||||
font-weight: 500; /* 调整按钮字重 */
|
font-weight: 500; /* 调整按钮字重 */
|
||||||
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, opacity 0.2s ease; /* 添加过渡 */
|
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, opacity 0.2s ease; /* 添加过渡 */
|
||||||
|
|||||||
Reference in New Issue
Block a user