Merge pull request 'feat:添加新增虚拟机' (#3) from master into deploy
Build and Deploy Vue3 / build (push) Successful in 2m53s
Build and Deploy Vue3 / deploy (push) Successful in 24m39s

Reviewed-on: lin/ApiServer-Web-admin_dashboard_pc#3
This commit was merged in pull request #3.
This commit is contained in:
2025-10-01 19:01:36 +08:00
2 changed files with 301 additions and 2 deletions
+8
View File
@@ -279,6 +279,14 @@ export const connectConsole = data => {
}
});
};
/**新增虚拟机 (管理员) */
export const addInstance = data => {
return http2.post("/v1/admin/instance/create_vm", data, {
headers: {
"Content-Type": "multipart/form-data"
}
});
};
/**获取虚拟机控制台 */
export const getInstanceConsole = data => {
return http2.get(`/v1/admin/instance/console/${data}`);
+293 -2
View File
@@ -14,6 +14,9 @@
<el-button @click="resetSearch">
<el-icon><refresh /></el-icon>重置
</el-button>
<el-button type="success" @click="showAddVmDialog">
<el-icon><plus /></el-icon>新增虚拟机
</el-button>
</div>
<el-table
@@ -124,6 +127,131 @@
@current-change="handleCurrentChange"
/>
</div>
<!-- 新增虚拟机对话框 -->
<el-dialog
v-model="addVmDialogVisible"
title="新增虚拟机"
width="600px"
:close-on-click-modal="false"
>
<el-form
ref="addVmFormRef"
:model="addVmForm"
:rules="addVmRules"
label-width="120px"
>
<el-form-item label="用户ID" prop="user_id">
<el-input
v-model="addVmForm.user_id"
placeholder="请输入用户ID"
clearable
/>
</el-form-item>
<el-form-item label="套餐" prop="plan_id">
<el-select
v-model="addVmForm.plan_id"
placeholder="请选择套餐"
style="width: 100%"
clearable
:loading="planLoading"
@focus="fetchPlanList"
>
<el-option
v-for="plan in planList"
:key="plan.id"
:label="plan.name"
:value="plan.plan_id"
>
<span style="float: left">{{ plan.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">
{{ plan.price }}/
</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="额外数据卷大小" prop="volume_size">
<el-input-number
v-model="addVmForm.volume_size"
:min="0"
:max="10000"
placeholder="请输入额外数据卷大小(GB)"
style="width: 100%"
controls-position="right"
/>
</el-form-item>
<el-form-item label="购买时间(月)" prop="pay_months">
<el-input-number
v-model="addVmForm.pay_months"
:min="1"
:max="120"
placeholder="请输入购买月数"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="支付类型" prop="pay_type">
<el-select
v-model="addVmForm.pay_type"
placeholder="请选择支付类型"
style="width: 100%"
clearable
>
<!-- <el-option label="支付宝" value="alipay" />
<el-option label="微信支付" value="wechat" />
<el-option label="银行卡" value="bank" /> -->
<el-option label="余额" value="0" />
</el-select>
</el-form-item>
<el-form-item label="镜像" prop="image_id">
<el-select
v-model="addVmForm.image_id"
placeholder="请选择镜像"
style="width: 100%"
clearable
:loading="mirrorLoading"
@focus="fetchMirrorList"
>
<el-option
v-for="mirror in mirrorList"
:key="mirror.id"
:label="mirror.name"
:value="mirror.id"
>
<span style="float: left">{{ mirror.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">
{{ mirror.size }}MB
</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number
v-model="addVmForm.price"
:min="0"
:max="999999"
:precision="2"
placeholder="请输入价格"
style="width: 100%"
controls-position="right"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelAddVm">取消</el-button>
<el-button type="primary" @click="confirmAddVm" :loading="addVmLoading">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -132,7 +260,7 @@ import { ref, onMounted, defineProps, watch } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
import {
Search, Refresh, Menu, VideoPlay, VideoPause, RefreshRight, ArrowDown
Search, Refresh, Menu, VideoPlay, VideoPause, RefreshRight, ArrowDown, Plus
} from '@element-plus/icons-vue';
import {
getContainer,
@@ -142,8 +270,11 @@ import {
getInstanceList,
reinstallI,
rescueInstance,
exitRescueInstance
exitRescueInstance,
addInstance,
getServerPlan
} from '@/utils/acs/server';
import { getMirrorList } from '@/utils/acs/mirror';
const props = defineProps({
ID: {
@@ -160,6 +291,48 @@ const currentPage = ref(1);
const pageSize = ref(10);
const searchKey = ref('');
// 新增虚拟机相关状态
const addVmDialogVisible = ref(false);
const addVmLoading = ref(false);
const addVmFormRef = ref(null);
const addVmForm = ref({
user_id: '',
plan_id: '',
volume_size: null,
pay_months: null,
pay_type: '',
image_id: '',
price: null
});
// 套餐和镜像数据
const planList = ref([]);
const mirrorList = ref([]);
const planLoading = ref(false);
const mirrorLoading = ref(false);
// 表单验证规则
const addVmRules = ref({
user_id: [
{ required: true, message: '请输入用户ID', trigger: 'blur' }
],
plan_id: [
{ required: true, message: '请选择套餐', trigger: 'change' }
],
pay_months: [
{ required: true, message: '请输入购买月数', trigger: 'blur' }
],
pay_type: [
{ required: true, message: '请选择支付类型', trigger: 'change' }
],
image_id: [
{ required: true, message: '请选择镜像', trigger: 'change' }
],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' }
]
});
// 获取虚拟机列表
const fetchVmList = async () => {
loading.value = true;
@@ -437,6 +610,124 @@ watch(() => props.ID, (newVal) => {
}
});
// 获取套餐列表
const fetchPlanList = async () => {
if (planList.value.length > 0) return; // 已加载过,不重复加载
planLoading.value = true;
try {
const response = await getServerPlan({
server_id: props.ID,
count: 100
});
if (response && response.data && response.data.code === 200) {
planList.value = response.data.data || [];
} else {
ElMessage.error('获取套餐列表失败');
}
} catch (error) {
console.error('获取套餐列表出错:', error);
ElMessage.error('获取套餐列表出错');
} finally {
planLoading.value = false;
}
};
// 获取镜像列表
const fetchMirrorList = async () => {
if (mirrorList.value.length > 0) return; // 已加载过,不重复加载
mirrorLoading.value = true;
try {
const response = await getMirrorList(props.ID);
if (response && response.data && response.data.code === 200) {
mirrorList.value = response.data.data || [];
} else {
ElMessage.error('获取镜像列表失败');
}
} catch (error) {
console.error('获取镜像列表出错:', error);
ElMessage.error('获取镜像列表出错');
} finally {
mirrorLoading.value = false;
}
};
// 显示新增虚拟机对话框
const showAddVmDialog = () => {
addVmDialogVisible.value = true;
// 重置表单
addVmForm.value = {
user_id: '',
plan_id: '',
volume_size: null,
pay_months: null,
pay_type: '',
image_id: '',
price: null
};
// 清除验证
if (addVmFormRef.value) {
addVmFormRef.value.clearValidate();
}
// 预加载数据
fetchPlanList();
fetchMirrorList();
};
// 取消新增虚拟机
const cancelAddVm = () => {
addVmDialogVisible.value = false;
};
// 确认新增虚拟机
const confirmAddVm = async () => {
if (!addVmFormRef.value) return;
try {
// 验证表单
await addVmFormRef.value.validate();
addVmLoading.value = true;
// 准备API参数
const apiParams = {
user_id: addVmForm.value.user_id,
plan_id: addVmForm.value.plan_id,
volume_size: addVmForm.value.volume_size?.toString() || '',
pay_months: addVmForm.value.pay_months?.toString() || '',
pay_type: addVmForm.value.pay_type,
image_id: addVmForm.value.image_id,
price: addVmForm.value.price?.toString() || ''
};
console.log('新增虚拟机参数:', apiParams);
// 调用API
const response = await addInstance(apiParams);
if (response && response.data && response.data.code === 200) {
ElMessage.success('虚拟机创建成功');
addVmDialogVisible.value = false;
// 刷新列表
fetchVmList();
} else {
ElMessage.error('创建失败: ' + (response.data?.message || '未知错误'));
}
} catch (error) {
console.error('新增虚拟机出错:', error);
if (error.message) {
ElMessage.error('表单验证失败: ' + error.message);
} else {
ElMessage.error('新增虚拟机出错');
}
} finally {
addVmLoading.value = false;
}
};
onMounted(() => {
if (props.ID) {
fetchVmList();