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
+187 -122
View File
@@ -1,103 +1,103 @@
<template>
<div class="image-requests-container">
<div class="page-header">
<h2>申请镜像</h2>
<div class="header-actions">
<el-button type="primary" @click="handleAdd">
<el-icon><plus /></el-icon>申请镜像
</el-button>
<el-button @click="handleRefresh">
<el-icon><refresh /></el-icon>刷新
</el-button>
<el-card class="main-container" shadow="never">
<!-- 搜索和操作栏 -->
<div class="filter-section">
<div class="filter-content">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="镜像名称">
<el-input v-model="searchForm.name" placeholder="请输入镜像名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="镜像类型">
<el-select v-model="searchForm.type" placeholder="请选择镜像类型" clearable style="width: 150px">
<el-option label="Docker镜像" value="docker" />
<el-option label="Windows镜像" value="windows" />
</el-select>
</el-form-item>
<el-form-item label="申请状态">
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable style="width: 150px">
<el-option label="已通过" value="approved" />
<el-option label="审核中" value="pending" />
<el-option label="已拒绝" value="rejected" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">
<el-icon><search /></el-icon>搜索
</el-button>
<el-button @click="resetSearch">
<el-icon><refresh /></el-icon>重置
</el-button>
</el-form-item>
</el-form>
<div class="action-bar">
<el-button type="primary" @click="handleAdd">
<el-icon><plus /></el-icon>申请镜像
</el-button>
<el-button @click="handleRefresh">
<el-icon><refresh /></el-icon>刷新
</el-button>
</div>
</div>
</div>
</div>
<!-- 提示信息 -->
<el-alert
type="info"
show-icon
:closable="false"
class="info-alert"
>
<el-icon><info-filled /></el-icon>
申请的镜像需要经过安全审核审核通过后可在创建云电脑或容器时使用审核结果将通过站内信通知
</el-alert>
<!-- 搜索区域 -->
<el-card class="search-card">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="镜像名称">
<el-input v-model="searchForm.name" placeholder="请输入镜像名称" clearable />
</el-form-item>
<el-form-item label="镜像类型">
<el-select v-model="searchForm.type" placeholder="请选择镜像类型" clearable>
<el-option label="Docker镜像" value="docker" />
<el-option label="Windows镜像" value="windows" />
</el-select>
</el-form-item>
<el-form-item label="申请状态">
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
<el-option label="已通过" value="approved" />
<el-option label="审核中" value="pending" />
<el-option label="已拒绝" value="rejected" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">
<el-icon><search /></el-icon>搜索
</el-button>
<el-button @click="resetSearch">
<el-icon><refresh /></el-icon>重置
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card class="table-card">
<el-table
v-loading="loading"
:data="tableData"
border
style="width: 100%"
row-key="id"
<!-- 提示信息 -->
<el-alert
type="info"
show-icon
:closable="false"
class="info-alert"
style="margin: 20px 20px 0; width: auto;"
>
<el-table-column prop="id" label="申请ID" width="150" align="center" />
<el-table-column prop="name" label="镜像名称" min-width="180" show-overflow-tooltip />
<el-table-column prop="type" label="类型" width="120" align="center">
<template #default="scope">
<el-tag :type="scope.row.type === 'docker' ? 'success' : 'primary'">
{{ scope.row.type === 'docker' ? 'Docker' : 'Windows' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="requestTime" label="申请时间" width="180" align="center" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" link @click="handleView(scope.row)">
<el-icon><view /></el-icon>查看详情
</el-button>
<el-button
v-if="scope.row.status === 'rejected'"
type="primary"
link
@click="handleResubmit(scope.row)"
>
<el-icon><refresh /></el-icon>重新提交
</el-button>
</template>
</el-table-column>
</el-table>
<template #title>
申请的镜像需要经过安全审核审核通过后可在创建云电脑或容器时使用审核结果将通过站内信通知
</template>
</el-alert>
<!-- 分页 -->
<div class="pagination-container">
<!-- 数据表格 -->
<div class="table-section">
<el-table
v-loading="loading"
:data="tableData"
style="width: 100%"
row-key="id"
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
>
<el-table-column prop="id" label="申请ID" width="150" align="center" />
<el-table-column prop="name" label="镜像名称" min-width="180" show-overflow-tooltip />
<el-table-column prop="type" label="类型" width="120" align="center">
<template #default="scope">
<el-tag :type="scope.row.type === 'docker' ? 'success' : 'primary'">
{{ scope.row.type === 'docker' ? 'Docker' : 'Windows' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="requestTime" label="申请时间" width="180" align="center" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" link @click="handleView(scope.row)">
<el-icon><view /></el-icon>查看详情
</el-button>
<el-button
v-if="scope.row.status === 'rejected'"
type="primary"
link
@click="handleResubmit(scope.row)"
>
<el-icon><refresh /></el-icon>重新提交
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
@@ -106,6 +106,8 @@
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
background
class="pagination"
/>
</div>
</el-card>
@@ -589,41 +591,58 @@ onMounted(() => {
<style scoped>
.image-requests-container {
padding: 20px;
padding: 0;
}
.page-header {
.main-container {
border: 1px solid #e1e8ed;
background: #ffffff;
}
.filter-section {
padding: 0;
border-bottom: 1px solid #e1e8ed;
background: #fafbfc;
}
.filter-content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.header-actions {
display: flex;
gap: 10px;
}
.info-alert {
margin-bottom: 20px;
}
.search-card {
margin-bottom: 20px;
}
.search-form {
display: flex;
padding: 16px 20px;
gap: 20px;
flex-wrap: wrap;
}
.table-card {
margin-bottom: 20px;
.search-form {
margin: 0;
flex: 1;
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.pagination-container {
margin-top: 20px;
.search-form :deep(.el-form-item) {
margin-bottom: 0;
margin-right: 12px;
}
.action-bar {
display: flex;
gap: 12px;
flex-shrink: 0;
}
.table-section {
padding: 0;
}
.pagination {
margin-top: 20px;
padding: 16px 20px;
border-top: 1px solid #e1e8ed;
background: #fafbfc;
justify-content: flex-end;
}
@@ -637,12 +656,16 @@ onMounted(() => {
/* 环境变量配置样式 */
.env-vars-container {
margin-bottom: 20px;
background-color: #f8f9fa;
padding: 16px;
border-radius: 4px;
}
.env-vars-header {
display: flex;
margin-bottom: 10px;
font-weight: bold;
font-weight: 600;
color: #606266;
}
.env-vars-item {
@@ -665,17 +688,23 @@ onMounted(() => {
.add-env-btn {
margin-top: 10px;
width: 100%;
border-style: dashed;
}
/* 端口配置样式 */
.ports-container {
margin-bottom: 20px;
background-color: #f8f9fa;
padding: 16px;
border-radius: 4px;
}
.ports-header {
display: flex;
margin-bottom: 10px;
font-weight: bold;
font-weight: 600;
color: #606266;
}
.ports-item {
@@ -702,6 +731,8 @@ onMounted(() => {
.add-port-btn {
margin-top: 10px;
width: 100%;
border-style: dashed;
}
/* 详情样式 */
@@ -710,11 +741,13 @@ onMounted(() => {
}
.request-reason {
background-color: #f8f8f8;
background-color: #f8f9fa;
padding: 15px;
border-radius: 4px;
margin-top: 10px;
white-space: pre-wrap;
color: #606266;
line-height: 1.6;
}
.review-comment {
@@ -724,5 +757,37 @@ onMounted(() => {
margin-top: 10px;
white-space: pre-wrap;
border-left: 4px solid #67c23a;
color: #606266;
}
</style>
/* 表格样式优化 */
:deep(.el-table) {
border: none;
color: #2c3e50;
}
:deep(.el-table__header) {
background: #f8f9fa;
}
:deep(.el-table th) {
background: #f8f9fa !important;
border-bottom: 2px solid #e1e8ed;
color: #2c3e50;
font-weight: 600;
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;
}
</style>