style: reorganize key list layout — actions to bottom, fix footer wrap
Layout restructure: - Move Import Keys button from toolbar to footer (left side) - Move batch action bar (select-all + export/delete) to bottom, above the footer, below the tree view - Toolbar now only contains filter input + refresh/toggle buttons Footer fix: - Add min-width:0 + overflow:hidden on left side to truncate long counts - Add flex-shrink:0 + white-space:nowrap on right side to keep buttons from wrapping when key count is large
This commit is contained in:
@@ -561,20 +561,6 @@ onUnmounted(() => {
|
|||||||
<el-icon class="search-icon"><Filter /></el-icon>
|
<el-icon class="search-icon"><Filter /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-button size="small" @click="importKeys" :disabled="!connStore.activeId" class="import-btn">
|
|
||||||
{{ t('key.importKeys') }}
|
|
||||||
</el-button>
|
|
||||||
<div v-if="multiSelect" class="select-all-wrapper">
|
|
||||||
<label class="select-all-label" @click.prevent="selectAll">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="select-all-checkbox"
|
|
||||||
:checked="multiSelect && selectedKeys.size === filteredKeys.length && filteredKeys.length > 0"
|
|
||||||
:indeterminate="multiSelect && selectedKeys.size > 0 && selectedKeys.size < filteredKeys.length"
|
|
||||||
/>
|
|
||||||
<span>{{ t('key.selectAll') }}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<el-button-group class="key-actions">
|
<el-button-group class="key-actions">
|
||||||
<el-button size="small" @click="refreshKeys" :disabled="!connStore.activeId">
|
<el-button size="small" @click="refreshKeys" :disabled="!connStore.activeId">
|
||||||
<el-icon><Refresh /></el-icon>
|
<el-icon><Refresh /></el-icon>
|
||||||
@@ -589,18 +575,6 @@ onUnmounted(() => {
|
|||||||
</el-button-group>
|
</el-button-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="multiSelect && selectedKeys.size > 0" class="batch-bar">
|
|
||||||
<span class="batch-count">{{ t('key.selected', { n: selectedKeys.size }) }}</span>
|
|
||||||
<div class="batch-bar-actions">
|
|
||||||
<el-button size="small" @click="exportKeys">
|
|
||||||
{{ t('key.exportKeys') }}
|
|
||||||
</el-button>
|
|
||||||
<el-button size="small" type="danger" @click="batchDelete">
|
|
||||||
{{ t('key.batchDelete') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="key-content">
|
<div class="key-content">
|
||||||
<!-- Tree View -->
|
<!-- Tree View -->
|
||||||
<div class="key-tree">
|
<div class="key-tree">
|
||||||
@@ -642,6 +616,30 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Batch actions bar (bottom) -->
|
||||||
|
<div v-if="multiSelect && selectedKeys.size > 0" class="batch-bar">
|
||||||
|
<div class="batch-bar-left">
|
||||||
|
<label class="select-all-label" @click.prevent="selectAll">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="select-all-checkbox"
|
||||||
|
:checked="multiSelect && selectedKeys.size === filteredKeys.length && filteredKeys.length > 0"
|
||||||
|
:indeterminate="multiSelect && selectedKeys.size > 0 && selectedKeys.size < filteredKeys.length"
|
||||||
|
/>
|
||||||
|
<span>{{ t('key.selectAll') }}</span>
|
||||||
|
</label>
|
||||||
|
<span class="batch-count">{{ t('key.selected', { n: selectedKeys.size }) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="batch-bar-actions">
|
||||||
|
<el-button size="small" @click="exportKeys">
|
||||||
|
{{ t('key.exportKeys') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="batchDelete">
|
||||||
|
{{ t('key.batchDelete') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Right-click context menu -->
|
<!-- Right-click context menu -->
|
||||||
<div
|
<div
|
||||||
v-if="contextMenu.visible"
|
v-if="contextMenu.visible"
|
||||||
@@ -674,7 +672,12 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="key-footer">
|
<div class="key-footer">
|
||||||
<span class="key-count">{{ t('key.count', { n: keyStore.keys.length }) }}</span>
|
<div class="key-footer-left">
|
||||||
|
<el-button size="small" @click="importKeys" :disabled="!connStore.activeId">
|
||||||
|
{{ t('key.importKeys') }}
|
||||||
|
</el-button>
|
||||||
|
<span class="key-count">{{ t('key.count', { n: keyStore.keys.length }) }}</span>
|
||||||
|
</div>
|
||||||
<div class="key-footer-actions">
|
<div class="key-footer-actions">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="keyStore.hasMore && keyStore.loading"
|
v-if="keyStore.hasMore && keyStore.loading"
|
||||||
@@ -738,15 +741,21 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.batch-bar {
|
.batch-bar {
|
||||||
padding: 8px 12px;
|
padding: 6px 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--accent-bg);
|
background: var(--accent-bg);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.batch-bar-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.batch-bar-actions {
|
.batch-bar-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
@@ -759,10 +768,6 @@ onUnmounted(() => {
|
|||||||
color: var(--accent-light);
|
color: var(--accent-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-btn {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-content {
|
.key-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -859,23 +864,37 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.key-footer {
|
.key-footer {
|
||||||
padding: 8px 12px;
|
padding: 6px 12px;
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-footer-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-count {
|
.key-count {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-footer-actions {
|
.key-footer-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Right-click context menu */
|
/* Right-click context menu */
|
||||||
@@ -929,13 +948,7 @@ onUnmounted(() => {
|
|||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Select All checkbox */
|
/* Select All checkbox (in batch bar) */
|
||||||
.select-all-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-all-label {
|
.select-all-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user