refactor: extract image form to standalone page and implement tags view store

- Created ImageForm.vue as standalone page for add/edit image functionality
- Removed dialog-based image form from VmImages.vue
- Implemented tagsViewStore for global tab state management
- Added automatic tab closing on form cancel/back
- Fixed data persistence issue when switching between image edits
- Removed quick actions section from ImageForm
- Updated router configuration for new image form route
This commit is contained in:
2025-11-28 14:15:29 +08:00
parent 067e0539ba
commit f7c3be1d30
45 changed files with 8776 additions and 6881 deletions
+129 -196
View File
@@ -446,8 +446,8 @@
<!-- <serverChart v-if="TypeData" :Type="TypeData" class="chart-section" /> -->
<!-- 主要内容区域 -->
<div class="content-wrapper">
<el-tabs type="border-card" class="main-tabs">
<el-card class="main-container" shadow="never">
<el-tabs class="main-tabs">
<!-- 实例规格列表 -->
<el-tab-pane label="实例规格列表">
<div class="tab-header">
@@ -464,11 +464,11 @@
<el-table
v-loading="specLoading"
:data="spec_list"
border
stripe
style="width: 100%"
table-layout="auto"
class="data-table"
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
>
<el-table-column prop="plan_id" label="规格ID" width="80" />
<el-table-column prop="name" label="规格名称" min-width="120" show-overflow-tooltip />
@@ -510,24 +510,24 @@
</template>
</el-table-column>
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
<el-table-column label="操作" width="160" fixed="right">
<el-table-column label="操作" width="160" fixed="right" align="center">
<template #default="scope">
<div class="table-actions">
<el-tooltip content="编辑" placement="top" :hide-after="1500">
<el-button
type="primary"
circle
link
:icon="Edit"
@click="show_spec(scope.row); centerDialogVisible = true; addOrChange = false;"
/>
>编辑</el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" :hide-after="1500">
<el-button
type="danger"
circle
link
:icon="Delete"
@click="deleteSpec(scope.row.plan_id)"
/>
>删除</el-button>
</el-tooltip>
</div>
</template>
@@ -562,9 +562,9 @@
<el-table
:data="user_servers"
stripe
border
style="width: 100%"
class="data-table"
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
>
<el-table-column label="ID" prop="container_id" width="80" />
<el-table-column label="价格" prop="pay" width="100">
@@ -598,7 +598,7 @@
: scope.row.container_state == 4
? 'danger'
: 'info'"
effect="light"
effect="plain"
>
{{
scope.row.container_state == 0
@@ -616,11 +616,11 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<el-table-column label="操作" width="100" fixed="right" align="center">
<template #default="scope">
<el-button
type="primary"
size="small"
link
:icon="Setting"
@click="$router.push('/servers/container?container_id=' + scope.row.container_id)"
>
@@ -667,9 +667,9 @@
<el-table
:data="floatList"
style="width: 100%"
border
stripe
class="data-table"
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
>
<el-table-column label="ID" prop="id" width="80" />
<el-table-column label="创建时间" min-width="160">
@@ -680,7 +680,7 @@
<el-table-column label="浮动IP" prop="floating_ip" min-width="150" />
<el-table-column label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.is_used ? 'success' : 'info'" effect="light">
<el-tag :type="scope.row.is_used ? 'success' : 'info'" effect="plain">
{{ scope.row.is_used ? "已绑定" : "未绑定" }}
</el-tag>
</template>
@@ -690,10 +690,10 @@
<el-tooltip content="删除IP" placement="top" :hide-after="1500">
<el-button
type="danger"
circle
link
:icon="Delete"
@click="delFloating(scope.row.id)"
/>
>删除</el-button>
</el-tooltip>
</template>
</el-table-column>
@@ -702,7 +702,7 @@
<el-empty v-if="floatList.length === 0" description="暂无浮动IP数据" />
</el-tab-pane>
</el-tabs>
</div>
</el-card>
<!-- 添加/编辑实例规格对话框 -->
<el-dialog
@@ -2617,9 +2617,7 @@ import { ElMessageBox } from 'element-plus';
<style scoped>
.server-container {
padding: 20px;
background-color: #f5f7fa;
min-height: calc(100vh - 120px);
padding: 0;
}
/* 页面标题区域 */
@@ -2628,6 +2626,9 @@ import { ElMessageBox } from 'element-plus';
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding: 16px 20px;
background: #fff;
border-bottom: 1px solid #e1e8ed;
}
.page-header .left {
@@ -2638,7 +2639,7 @@ import { ElMessageBox } from 'element-plus';
.page-header .title {
margin: 0;
font-size: 24px;
font-size: 20px;
font-weight: 600;
color: #303133;
}
@@ -2647,7 +2648,8 @@ import { ElMessageBox } from 'element-plus';
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
padding: 4px 12px;
border-radius: 4px;
}
.status-dot {
@@ -2669,13 +2671,13 @@ import { ElMessageBox } from 'element-plus';
/* 返回按钮样式 */
.back-btn {
font-size: 16px;
color: #409EFF;
padding: 8px 0;
margin-right: 16px;
color: #606266;
padding: 0;
margin-right: 8px;
}
.back-btn:hover {
color: #66b1ff;
color: #409EFF;
}
/* 服务器信息卡片 */
@@ -2684,6 +2686,7 @@ import { ElMessageBox } from 'element-plus';
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 24px;
padding: 0 20px;
}
/* 服务器详细信息卡片 */
@@ -2692,16 +2695,17 @@ import { ElMessageBox } from 'element-plus';
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px;
padding: 0 20px;
}
.info-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
padding: 0;
overflow: hidden;
transition: all 0.3s;
border: 1px solid #ebeef5;
border: 1px solid #e1e8ed;
}
.info-card:hover {
@@ -2710,10 +2714,10 @@ import { ElMessageBox } from 'element-plus';
}
.card-title {
background-color: #f5f7fa;
background-color: #fafbfc;
padding: 12px 16px;
border-bottom: 1px solid #ebeef5;
font-size: 16px;
border-bottom: 1px solid #e1e8ed;
font-size: 15px;
font-weight: 600;
color: #303133;
display: flex;
@@ -2722,7 +2726,7 @@ import { ElMessageBox } from 'element-plus';
}
.card-title .el-icon {
font-size: 18px;
font-size: 16px;
color: #409EFF;
}
@@ -2745,32 +2749,31 @@ import { ElMessageBox } from 'element-plus';
}
.info-value {
font-size: 15px;
font-size: 14px;
color: #303133;
word-break: break-all;
}
.info-value.highlight {
font-weight: 600;
font-size: 16px;
color: #409EFF;
}
/* 硬件信息样式 - 符合整体设计风格 */
/* 硬件信息样式 */
.device-count {
color: #909399;
font-size: 14px;
font-size: 13px;
font-weight: normal;
margin-left: 8px;
}
.hardware-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 16px;
margin-bottom: 20px;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 12px;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #ebeef5;
border-bottom: 1px solid #f0f2f5;
}
.summary-item {
@@ -2780,21 +2783,21 @@ import { ElMessageBox } from 'element-plus';
.hardware-devices {
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
}
.device-item {
padding: 16px;
padding: 12px;
background-color: #fafbfc;
border-radius: 6px;
border: 1px solid #e4e7ed;
border-radius: 4px;
border: 1px solid #e1e8ed;
}
.device-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
margin-bottom: 8px;
}
.device-title {
@@ -2807,57 +2810,27 @@ import { ElMessageBox } from 'element-plus';
.device-title .el-icon {
margin-right: 6px;
font-size: 16px;
font-size: 14px;
}
.device-details {
margin-top: 12px;
margin-top: 8px;
}
.detail-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
margin-bottom: 12px;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 8px;
margin-bottom: 8px;
}
.usage-progress {
margin-top: 8px;
}
/* 使用率状态颜色类 */
.info-value.usage-normal {
color: #67C23A;
font-weight: 600;
}
.info-value.usage-medium {
color: #409EFF;
font-weight: 600;
}
.info-value.usage-warning {
color: #E6A23C;
font-weight: 600;
}
.info-value.usage-critical {
color: #F56C6C;
font-weight: 600;
}
/* 实际硬件划分样式 */
.highlight-item {
padding: 8px;
background-color: #f0f9ff;
border-radius: 4px;
border-left: 3px solid #409EFF;
margin-bottom: 16px;
}
/* 流量信息样式 */
.traffic-section {
margin-bottom: 20px;
margin-bottom: 16px;
}
.traffic-section:last-child {
@@ -2865,62 +2838,38 @@ import { ElMessageBox } from 'element-plus';
}
.section-title {
font-size: 14px;
font-size: 13px;
font-weight: 600;
margin-bottom: 12px;
margin-bottom: 8px;
padding-bottom: 4px;
border-bottom: 1px solid #ebeef5;
border-bottom: 1px solid #f0f2f5;
color: #606266;
}
.traffic-speed-grid,
.traffic-total-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
}
.traffic-total-grid {
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}
.traffic-total-grid,
.traffic-distribution {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
}
.raw-data {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
padding: 12px;
background-color: #fafbfc;
border-radius: 6px;
border: 1px solid #e4e7ed;
border-radius: 4px;
border: 1px solid #e1e8ed;
font-family: 'Courier New', monospace;
}
.raw-data .info-value {
font-size: 13px;
font-size: 12px;
color: #606266;
}
.traffic-note {
margin-top: 8px;
}
/* 旧样式保持兼容 */
.usage-high {
color: #F56C6C;
font-weight: 600;
}
.usage-medium {
color: #E6A23C;
font-weight: 600;
}
/* 错误状态样式 */
.error-status {
color: #F56C6C;
@@ -2929,15 +2878,10 @@ import { ElMessageBox } from 'element-plus';
}
/* 主要内容区域 */
.chart-section {
margin-bottom: 24px;
}
.content-wrapper {
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
margin-bottom: 24px;
.main-container {
margin: 0 20px 20px 20px;
border: 1px solid #e1e8ed;
background: #ffffff;
}
.tab-header {
@@ -2945,11 +2889,13 @@ import { ElMessageBox } from 'element-plus';
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 16px 20px;
border-bottom: 1px solid #f0f2f5;
}
.tab-title {
margin: 0;
font-size: 18px;
font-size: 16px;
font-weight: 600;
color: #303133;
}
@@ -2969,40 +2915,67 @@ import { ElMessageBox } from 'element-plus';
gap: 8px;
}
/* 表格样式 */
.data-table {
margin-bottom: 16px;
/* 表格样式优化 */
:deep(.el-table) {
border: none;
color: #2c3e50;
}
.table-actions {
display: flex;
justify-content: center;
gap: 8px;
:deep(.el-table__header) {
background: #f8f9fa;
}
.resource-value {
white-space: nowrap;
}
.unit {
color: #909399;
font-size: 12px;
margin-left: 2px;
}
.price-tag {
:deep(.el-table th) {
background: #f8f9fa !important;
border-bottom: 2px solid #e1e8ed;
color: #2c3e50;
font-weight: 600;
color: #F56C6C;
font-size: 13px;
}
.time-info {
font-size: 13px;
:deep(.el-table td) {
border-bottom: 1px solid #f0f2f5;
color: #34495e;
}
:deep(.el-table tr:hover > td) {
background-color: #f8f9fa !important;
}
:deep(.el-card__body) {
padding: 0;
}
/* Tabs 样式优化 */
:deep(.el-tabs__header) {
margin: 0;
border-bottom: 1px solid #e1e8ed;
background: #fff;
padding: 0 20px;
}
:deep(.el-tabs__item) {
height: 48px;
line-height: 48px;
font-weight: 500;
color: #606266;
}
:deep(.el-tabs__item.is-active) {
color: #409EFF;
background: #fff;
font-weight: 600;
}
:deep(.el-tabs__content) {
padding: 0;
}
/* 分页样式 */
.pagination-container {
margin-top: 20px;
padding: 16px 20px;
border-top: 1px solid #e1e8ed;
background: #fafbfc;
display: flex;
justify-content: flex-end;
}
@@ -3013,15 +2986,13 @@ import { ElMessageBox } from 'element-plus';
}
.section-title {
font-size: 16px;
font-size: 15px;
font-weight: 600;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px dashed #ebeef5;
border-bottom: 1px dashed #e1e8ed;
}
.dialog-footer {
display: flex;
justify-content: space-between;
@@ -3124,27 +3095,24 @@ import { ElMessageBox } from 'element-plus';
gap: 16px;
}
.page-header .left {
flex-wrap: wrap;
}
.back-btn {
margin-right: 8px;
margin-bottom: 8px;
}
.server-info {
grid-template-columns: 1fr;
padding: 0 16px;
}
.server-detail-info {
grid-template-columns: 1fr;
padding: 0 16px;
}
.info-card.location-info {
grid-column: auto;
}
.main-container {
margin: 0 16px 16px 16px;
}
.tab-header {
flex-direction: column;
align-items: flex-start;
@@ -3159,40 +3127,5 @@ import { ElMessageBox } from 'element-plus';
width: 100%;
justify-content: space-between;
}
/* 硬件信息响应式 */
.hardware-summary {
grid-template-columns: 1fr;
gap: 12px;
}
.detail-row {
grid-template-columns: 1fr;
gap: 8px;
}
.device-item {
padding: 12px;
}
.device-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
/* 流量信息响应式 */
.traffic-speed-grid,
.traffic-total-grid,
.traffic-distribution {
grid-template-columns: 1fr;
gap: 8px;
}
.raw-data {
grid-template-columns: 1fr;
gap: 8px;
padding: 8px;
}
}
</style>