From 40a5e486a67f9e707907d9630d5854a65b96c59e Mon Sep 17 00:00:00 2001
From: 2256907009 <2256907009@qq.com>
Date: Tue, 24 Mar 2026 18:57:52 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=B9=E6=8E=A5=E7=94=A8=E6=88=B7?=
=?UTF-8?q?=E7=BB=84=E7=BD=91=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/admin/kvmService.js | 13 +-
.../admin/HostGroupSelectorPopup.vue | 1 +
src/components/admin/ImageSelectorPopup.vue | 3 +-
src/components/admin/NetworkSelectorPopup.vue | 19 +-
.../admin/SecurityGroupSelectorPopup.vue | 16 +-
src/components/admin/VmSelectorPopup.vue | 5 +-
src/components/admin/VolumeSelectorPopup.vue | 18 +-
src/router/index.js | 10 +
src/style.css | 17 +
src/views/acs/nodes/components/VmList.vue | 2 +-
src/views/acs/nodes/server.vue | 2 +-
src/views/virtualization/BackupManage.vue | 2 +
src/views/virtualization/HostDetail.vue | 53 +-
src/views/virtualization/HostTreeManage.vue | 2 +-
src/views/virtualization/ImageDetail.vue | 11 +-
src/views/virtualization/ImageManage.vue | 80 +-
src/views/virtualization/KvmServiceDetail.vue | 16 +-
src/views/virtualization/NetworkManage.vue | 4 +-
.../virtualization/SecurityGroupDetail.vue | 5 +-
.../virtualization/SecurityGroupManage.vue | 2 +-
src/views/virtualization/SnapshotManage.vue | 2 +
.../virtualization/UserNetworkingManage.vue | 139 +-
src/views/virtualization/VmDetail.vue | 1476 +--
src/views/virtualization/VmManage.vue | 126 +-
src/views/virtualization/VncNodeManage.vue | 2 +-
src/views/virtualization/VolumeDetail.vue | 291 +
src/views/virtualization/VolumeManage.vue | 112 +-
虚拟化平台管理图谱.md | 271 +
默认模块.openapi.json | 8576 +----------------
29 files changed, 1895 insertions(+), 9381 deletions(-)
create mode 100644 src/views/virtualization/VolumeDetail.vue
create mode 100644 虚拟化平台管理图谱.md
diff --git a/src/api/admin/kvmService.js b/src/api/admin/kvmService.js
index 54d41e6..4705a5e 100644
--- a/src/api/admin/kvmService.js
+++ b/src/api/admin/kvmService.js
@@ -672,42 +672,41 @@ export const setBackupLimit = (data) => {
/**
* ================================
* 用户组网管理 (UserNetworking)
- * 注意:此模块接口前缀为 /api/v1/admins/service/
* ================================
*/
/** 获取组网列表 */
export const getUserNetworkingList = (params) => {
- return http2.get('/api/v1/admins/service/host_service/point/networking/list', { params })
+ return http2.get('/api/v1/admin/server/host_service/point/networking/list', { params })
}
/** 获取组网详情 */
export const getUserNetworkingDetail = (params) => {
- return http2.get('/api/v1/admins/service/host_service/point/networking/detail', { params })
+ return http2.get('/api/v1/admin/server/host_service/point/networking/detail', { params })
}
/** 创建用户组网 */
export const createUserNetworking = (data) => {
- return http2.post('/api/v1/admins/service/host_service/point/networking/create', data, {
+ return http2.post('/api/v1/admin/server/host_service/point/networking/create', data, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
/** 为虚拟机分配组网 IP */
export const assignUserNetworking = (data) => {
- return http2.post('/api/v1/admins/service/host_service/point/networking/assign', data, {
+ return http2.post('/api/v1/admin/server/host_service/point/networking/assign', data, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
/** 删除组网 */
export const deleteUserNetworking = (params) => {
- return http2.delete('/api/v1/admins/service/host_service/point/networking/delete', { params })
+ return http2.delete('/api/v1/admin/server/host_service/point/networking/delete', { params })
}
/** 删除组网下的指定网络 */
export const removeUserNetworkingNetwork = (data) => {
- return http2.post('/api/v1/admins/service/host_service/point/networking/remove_network', data, {
+ return http2.post('/api/v1/admin/server/host_service/point/networking/remove_network', data, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
diff --git a/src/components/admin/HostGroupSelectorPopup.vue b/src/components/admin/HostGroupSelectorPopup.vue
index dfa062a..6913ce7 100644
--- a/src/components/admin/HostGroupSelectorPopup.vue
+++ b/src/components/admin/HostGroupSelectorPopup.vue
@@ -70,4 +70,5 @@ const handleClose = () => { selectedItem.value = null }
diff --git a/src/components/admin/ImageSelectorPopup.vue b/src/components/admin/ImageSelectorPopup.vue
index 3c33675..9158806 100644
--- a/src/components/admin/ImageSelectorPopup.vue
+++ b/src/components/admin/ImageSelectorPopup.vue
@@ -68,7 +68,7 @@ watch(visible, (val) => emit('update:modelValue', val))
const loadList = async () => {
loading.value = true
try {
- const params = { service_id: props.serviceId, page: 1, count: 100 }
+ const params = { service_id: props.serviceId, page: 1, count: 10 }
if (keyword.value) params.keyword = keyword.value
if (filterOsType.value) params.os_type = filterOsType.value
const res = await getImageList(params)
@@ -96,4 +96,5 @@ const handleClose = () => { selectedItem.value = null }
.selector-container { min-height: 200px; }
.filter-bar { display: flex; gap: 8px; margin-bottom: 12px; }
:deep(.current-row) { background-color: #ecf5ff !important; }
+:deep(.el-table__body tr) { cursor: pointer; }
diff --git a/src/components/admin/NetworkSelectorPopup.vue b/src/components/admin/NetworkSelectorPopup.vue
index 81b575b..d8e0c5b 100644
--- a/src/components/admin/NetworkSelectorPopup.vue
+++ b/src/components/admin/NetworkSelectorPopup.vue
@@ -35,8 +35,13 @@
- 取消
- 确认选择
+
@@ -52,7 +57,7 @@ const props = defineProps({
hostId: { type: Number, default: 0 }
})
-const emit = defineEmits(['update:modelValue', 'confirm'])
+const emit = defineEmits(['update:modelValue', 'confirm', 'create'])
const visible = ref(false)
const loading = ref(false)
@@ -63,6 +68,7 @@ const pageSize = ref(10)
const keyword = ref('')
const typeFilter = ref('')
const selectedItem = ref(null)
+const type = ref('bridge')
watch(() => props.modelValue, (val) => {
visible.value = val
@@ -82,7 +88,7 @@ const loadList = async () => {
if (!props.serviceId || !props.hostId) return
loading.value = true
try {
- const params = { service_id: props.serviceId, host_id: props.hostId, page: page.value, page_size: pageSize.value }
+ const params = { service_id: props.serviceId, host_id: props.hostId, page: page.value, page_size: pageSize.value,type: type.value }
if (keyword.value) params.keyword = keyword.value
if (typeFilter.value) params.type = typeFilter.value
const res = await getNetworkList(params)
@@ -103,6 +109,10 @@ const handleConfirm = () => {
}
}
const handleClose = () => { selectedItem.value = null }
+const handleCreate = () => {
+ visible.value = false
+ emit('create')
+}
diff --git a/src/components/admin/SecurityGroupSelectorPopup.vue b/src/components/admin/SecurityGroupSelectorPopup.vue
index 7c9610d..a36ca04 100644
--- a/src/components/admin/SecurityGroupSelectorPopup.vue
+++ b/src/components/admin/SecurityGroupSelectorPopup.vue
@@ -36,8 +36,13 @@
- 取消
- 确认选择
+
+
创建安全组
+
+ 取消
+ 确认选择
+
+
@@ -52,7 +57,7 @@ const props = defineProps({
serviceId: { type: Number, default: 0 }
})
-const emit = defineEmits(['update:modelValue', 'confirm'])
+const emit = defineEmits(['update:modelValue', 'confirm', 'create'])
const visible = ref(false)
const loading = ref(false)
@@ -92,6 +97,10 @@ const handleConfirm = () => {
if (selectedItem.value) { emit('confirm', selectedItem.value); visible.value = false }
}
const handleClose = () => { selectedItem.value = null }
+const handleCreate = () => {
+ visible.value = false
+ emit('create')
+}
diff --git a/src/components/admin/VmSelectorPopup.vue b/src/components/admin/VmSelectorPopup.vue
index a2787b5..f93e95d 100644
--- a/src/components/admin/VmSelectorPopup.vue
+++ b/src/components/admin/VmSelectorPopup.vue
@@ -45,7 +45,7 @@ const visible = ref(false)
const loading = ref(false)
const list = ref([])
const selectedItem = ref(null)
-const hostIdFilter = ref(0)
+const hostIdFilter = ref('')
const hostOptions = ref([])
watch(() => props.modelValue, (val) => {
@@ -56,7 +56,7 @@ watch(visible, (val) => emit('update:modelValue', val))
const loadHostOptions = async () => {
try {
- const res = await getRemoteHostList({ service_id: props.serviceId, page: 1, page_size: 100 })
+ const res = await getRemoteHostList({ service_id: props.serviceId, page: 1, page_size: 10 })
const body = res?.data
if (body?.code === 200 && body?.data) {
const inner = body.data
@@ -106,4 +106,5 @@ const handleClose = () => { selectedItem.value = null }
.selector-container { min-height: 200px; }
.filter-bar { display: flex; gap: 8px; margin-bottom: 12px; }
:deep(.current-row) { background-color: #ecf5ff !important; }
+:deep(.el-table__body tr) { cursor: pointer; }
diff --git a/src/components/admin/VolumeSelectorPopup.vue b/src/components/admin/VolumeSelectorPopup.vue
index 2309c44..c621218 100644
--- a/src/components/admin/VolumeSelectorPopup.vue
+++ b/src/components/admin/VolumeSelectorPopup.vue
@@ -43,8 +43,13 @@
- 取消
- 确认选择
+
+
创建数据卷
+
+ 取消
+ 确认选择
+
+
@@ -60,7 +65,7 @@ const props = defineProps({
hostId: { type: Number, default: 0 }
})
-const emit = defineEmits(['update:modelValue', 'confirm'])
+const emit = defineEmits(['update:modelValue', 'confirm', 'create'])
const visible = ref(false)
const loading = ref(false)
@@ -90,7 +95,7 @@ const loadList = async () => {
if (!props.serviceId || !props.hostId) return
loading.value = true
try {
- const params = { service_id: props.serviceId, host_id: props.hostId, page: page.value, page_size: pageSize.value }
+ const params = { service_id: props.serviceId, host_id: props.hostId, page: page.value, count: pageSize.value }
if (keyword.value) params.keyword = keyword.value
if (statusFilter.value) params.status = statusFilter.value
const res = await getVolumeList(params)
@@ -114,6 +119,10 @@ const handleConfirm = () => {
}
}
const handleClose = () => { selectedItem.value = null }
+const handleCreate = () => {
+ visible.value = false
+ emit('create')
+}
diff --git a/src/router/index.js b/src/router/index.js
index 2ceef4b..ce5cb74 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -534,6 +534,16 @@ const routes = [
hidden: true,
activeMenu: '/virtualization/kvm-service'
}
+ },
+ {
+ path: 'volume-detail',
+ name: 'VirtVolumeDetail',
+ component: () => import('../views/virtualization/VolumeDetail.vue'),
+ meta: {
+ title: '数据卷详情',
+ hidden: true,
+ activeMenu: '/virtualization/kvm-service'
+ }
}
]
},
diff --git a/src/style.css b/src/style.css
index 222de0c..3932200 100644
--- a/src/style.css
+++ b/src/style.css
@@ -114,6 +114,23 @@ body {
padding-right: 10px;
}
+/* 可点击元素统一手型光标 */
+.el-button,
+.el-button--link,
+.el-tag.is-closable .el-tag__close,
+.el-dropdown,
+.el-dropdown-menu__item,
+.el-switch,
+.el-checkbox,
+.el-radio,
+.el-select .el-input__wrapper,
+.el-table__body tr.el-table__row {
+ cursor: pointer;
+}
+.back-btn {
+ cursor: pointer;
+}
+
/* 响应式工具类 */
@media (max-width: 768px) {
.hidden-xs {
diff --git a/src/views/acs/nodes/components/VmList.vue b/src/views/acs/nodes/components/VmList.vue
index 5983064..4845074 100644
--- a/src/views/acs/nodes/components/VmList.vue
+++ b/src/views/acs/nodes/components/VmList.vue
@@ -618,7 +618,7 @@ const fetchPlanList = async () => {
try {
const response = await getServerPlan({
server_id: props.ID,
- count: 100
+ count: 10
});
if (response && response.data && response.data.code === 200) {
diff --git a/src/views/acs/nodes/server.vue b/src/views/acs/nodes/server.vue
index c08d2a1..f1ae628 100644
--- a/src/views/acs/nodes/server.vue
+++ b/src/views/acs/nodes/server.vue
@@ -2407,7 +2407,7 @@ const fetchContainerPlanList = async () => {
try {
const response = await getServerPlan({
server_id: route.query.server_id,
- count: 100
+ count: 10
});
console.log("获取容器套餐列表1111:",response);
diff --git a/src/views/virtualization/BackupManage.vue b/src/views/virtualization/BackupManage.vue
index 8464bc0..9308868 100644
--- a/src/views/virtualization/BackupManage.vue
+++ b/src/views/virtualization/BackupManage.vue
@@ -224,6 +224,8 @@ const handleProgress = async (row) => {
}
onMounted(() => { loadList() })
+
+defineExpose({ loadList })
diff --git a/src/views/virtualization/VmDetail.vue b/src/views/virtualization/VmDetail.vue
index c185f98..730dff9 100644
--- a/src/views/virtualization/VmDetail.vue
+++ b/src/views/virtualization/VmDetail.vue
@@ -27,12 +27,10 @@
重启
暂停
恢复
- 编辑虚拟机
+ 修改虚拟机
重构虚拟机
修改带宽
- 绑定安全组
- 解绑安全组
- 重建虚拟机
+ 重装虚拟机
救援模式
退出救援
@@ -52,7 +50,7 @@
IP地址
- {{ detail.ips || '-' }}
+ {{ publicIps || privateIps || detail.ips || '-' }}
创建时间
@@ -87,17 +85,24 @@
公网IP
- {{ detail.ips || '暂无' }}
+ {{ publicIps || '暂无' }}
+
+
+ 内网IP
+ {{ privateIps || '暂无' }}
下行/上行带宽
{{ detail.rx_bandwidth || 0 }} / {{ detail.tx_bandwidth || 0 }} Mbps
+
+
安全组
-
- {{ vmPortGroup.name }}
+
+ 入站: {{ vmPortGroup.name }}
+ 出站: {{ vmOutPortGroup.name }}
未绑定
@@ -124,7 +129,7 @@
流量上限(GB)
- {{ detail.traffic_max ?? '-' }}
+ {{ detail.traffic_max != null ? (detail.traffic_max / 1024 / 1024).toFixed(2) : '-' }}
快照配额
@@ -150,33 +155,38 @@
-
+
-
+
-
+
+
-
- {{ row.type || '-' }}
-
-
-
+
- 编辑
- 删除
+ {{ row.type === 'bridge' ? '网桥' : 'NAT' }}
+
+
+
+
+
+
+ 详情
+ 编辑
+ 删除
-
+