441 lines
11 KiB
Vue
441 lines
11 KiB
Vue
<template>
|
||
<dialog class="modal" :class="{ 'modal-open': visible }">
|
||
<div class="modal-box w-96 max-w-md">
|
||
<h3 class="text-lg font-bold mb-4">新增实验板</h3>
|
||
|
||
<!-- 步骤1: 输入板卡名称 -->
|
||
<div v-if="currentStep === 'input'" class="space-y-4">
|
||
<form @submit.prevent="handleSubmit">
|
||
<!-- 实验板名称 -->
|
||
<div class="form-control">
|
||
<label class="label">
|
||
<span class="label-text"
|
||
>实验板名称 <span class="text-error">*</span></span
|
||
>
|
||
</label>
|
||
<input
|
||
v-model="form.name"
|
||
type="text"
|
||
placeholder="请输入实验板名称"
|
||
class="input input-bordered"
|
||
:class="{ 'input-error': errors.name }"
|
||
required
|
||
/>
|
||
<label v-if="errors.name" class="label">
|
||
<span class="label-text-alt text-error">{{ errors.name }}</span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="modal-action">
|
||
<button
|
||
type="button"
|
||
class="btn btn-ghost"
|
||
@click="handleCancel"
|
||
:disabled="isSubmitting"
|
||
>
|
||
取消
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
class="btn btn-primary"
|
||
:class="{ loading: isSubmitting }"
|
||
:disabled="isSubmitting"
|
||
>
|
||
{{ isSubmitting ? "添加中..." : "确认添加" }}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 步骤2: 等待配对 -->
|
||
<div v-else-if="currentStep === 'pairing'" class="space-y-4">
|
||
<div class="text-center">
|
||
<div class="alert alert-info">
|
||
<div class="flex items-center">
|
||
<svg
|
||
class="w-5 h-5 mr-2"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||
/>
|
||
</svg>
|
||
<span>请打开实验板配对模式</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="modal-action">
|
||
<button
|
||
type="button"
|
||
class="btn btn-ghost"
|
||
@click="handleCancelPairing"
|
||
:disabled="isConfiguring"
|
||
>
|
||
取消
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="btn btn-primary"
|
||
@click="handlePairingConfirm"
|
||
:disabled="isConfiguring"
|
||
>
|
||
已开启
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 步骤3: 配置网络 -->
|
||
<div v-else-if="currentStep === 'configuring'" class="space-y-4">
|
||
<div class="text-center">
|
||
<div class="alert alert-warning">
|
||
<div class="flex items-center">
|
||
<div class="loading loading-spinner loading-sm mr-2"></div>
|
||
<span>正在分配网络...</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 步骤4: 显示配置结果 -->
|
||
<div v-else-if="currentStep === 'result'" class="space-y-4">
|
||
<div class="text-center">
|
||
<div class="alert alert-success">
|
||
<div class="flex items-center">
|
||
<svg
|
||
class="w-5 h-5 mr-2"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M5 13l4 4L19 7"
|
||
/>
|
||
</svg>
|
||
<span>实验板配置成功</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 网络配置信息 -->
|
||
<div v-if="networkConfig" class="space-y-2">
|
||
<h4 class="font-semibold">网络配置信息:</h4>
|
||
<div class="bg-base-200 p-3 rounded">
|
||
<div class="text-sm space-y-1">
|
||
<div>
|
||
<span class="font-medium">主机IP:</span>
|
||
{{ networkConfig.hostIP }}
|
||
</div>
|
||
<div>
|
||
<span class="font-medium">板卡IP:</span>
|
||
{{ networkConfig.boardIP }}
|
||
</div>
|
||
<div>
|
||
<span class="font-medium">主机MAC:</span>
|
||
{{ networkConfig.hostMAC }}
|
||
</div>
|
||
<div>
|
||
<span class="font-medium">板卡MAC:</span>
|
||
{{ networkConfig.boardMAC }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="modal-action">
|
||
<button type="button" class="btn btn-primary" @click="handleSuccess">
|
||
确认
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 点击背景关闭 -->
|
||
<form method="dialog" class="modal-backdrop">
|
||
<button type="button" @click="handleCancel">close</button>
|
||
</form>
|
||
</dialog>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, reactive, watch } from "vue";
|
||
import { AuthManager } from "../../utils/AuthManager";
|
||
import { useAlertStore } from "../../components/Alert";
|
||
import {
|
||
BoardStatus,
|
||
DataClient,
|
||
NetConfigClient,
|
||
type NetworkConfigDto,
|
||
} from "../../APIClient";
|
||
import { useRequiredInjection } from "@/utils/Common";
|
||
import { useBoardManager } from "@/utils/BoardManager";
|
||
|
||
// Props 和 Emits
|
||
interface Props {
|
||
visible: boolean;
|
||
}
|
||
|
||
interface Emits {
|
||
(e: "update:visible", value: boolean): void;
|
||
(e: "success"): void;
|
||
}
|
||
|
||
const props = defineProps<Props>();
|
||
const emit = defineEmits<Emits>();
|
||
|
||
// 使用 Alert
|
||
const alertStore = useAlertStore();
|
||
|
||
const boardManager = useRequiredInjection(useBoardManager);
|
||
|
||
// 当前步骤
|
||
const currentStep = ref<"input" | "pairing" | "configuring" | "result">(
|
||
"input",
|
||
);
|
||
|
||
// 表单数据
|
||
const form = reactive({
|
||
name: "Board1",
|
||
});
|
||
|
||
// 表单错误
|
||
const errors = reactive({
|
||
name: "",
|
||
});
|
||
|
||
// 状态
|
||
const isSubmitting = ref(false);
|
||
const isConfiguring = ref(false);
|
||
|
||
// 添加的板卡信息
|
||
const addedBoardId = ref<string>("");
|
||
const networkConfig = ref<NetworkConfigDto | null>(null);
|
||
|
||
// 验证表单
|
||
function validateForm(): boolean {
|
||
// 清空之前的错误
|
||
errors.name = "";
|
||
|
||
let isValid = true;
|
||
|
||
// 验证名称
|
||
if (!form.name.trim()) {
|
||
errors.name = "请输入实验板名称";
|
||
isValid = false;
|
||
} else if (form.name.trim().length < 2) {
|
||
errors.name = "实验板名称至少需要2个字符";
|
||
isValid = false;
|
||
} else if (form.name.trim().length > 50) {
|
||
errors.name = "实验板名称不能超过50个字符";
|
||
isValid = false;
|
||
}
|
||
|
||
return isValid;
|
||
}
|
||
|
||
// 重置表单
|
||
function resetForm() {
|
||
form.name = "Board1";
|
||
errors.name = "";
|
||
currentStep.value = "input";
|
||
addedBoardId.value = "";
|
||
networkConfig.value = null;
|
||
}
|
||
|
||
// 处理取消
|
||
function handleCancel() {
|
||
if (!isSubmitting.value && !isConfiguring.value) {
|
||
emit("update:visible", false);
|
||
resetForm();
|
||
}
|
||
}
|
||
|
||
// 处理提交
|
||
async function handleSubmit() {
|
||
if (!validateForm()) {
|
||
return;
|
||
}
|
||
|
||
isSubmitting.value = true;
|
||
|
||
try {
|
||
const dataClient = AuthManager.createClient(DataClient);
|
||
|
||
// 添加板卡到数据库
|
||
const boardId = await dataClient.addBoard(form.name.trim());
|
||
|
||
if (boardId) {
|
||
addedBoardId.value = boardId;
|
||
currentStep.value = "pairing";
|
||
alertStore?.success("板卡添加成功,请开启配对模式");
|
||
} else {
|
||
alertStore?.error("板卡添加失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("添加实验板失败:", error);
|
||
alertStore?.error("添加实验板失败");
|
||
} finally {
|
||
isSubmitting.value = false;
|
||
}
|
||
}
|
||
|
||
// 处理取消配对
|
||
async function handleCancelPairing() {
|
||
if (!addedBoardId.value) return;
|
||
|
||
try {
|
||
const dataClient = AuthManager.createClient(DataClient);
|
||
|
||
// 删除添加的板卡
|
||
await dataClient.deleteBoard(addedBoardId.value);
|
||
|
||
alertStore?.info("已取消添加实验板");
|
||
emit("update:visible", false);
|
||
resetForm();
|
||
} catch (error) {
|
||
console.error("删除板卡失败:", error);
|
||
alertStore?.error("删除板卡失败");
|
||
}
|
||
}
|
||
|
||
// 处理配对确认
|
||
async function handlePairingConfirm() {
|
||
if (!addedBoardId.value) return;
|
||
|
||
isConfiguring.value = true;
|
||
currentStep.value = "configuring";
|
||
|
||
try {
|
||
// 通过 AuthManager 获取认证的客户端
|
||
const dataClient = AuthManager.createClient(DataClient);
|
||
const netConfigClient = AuthManager.createClient(NetConfigClient);
|
||
|
||
// 获取数据库中对应分配的板卡信息
|
||
const boardInfo = await dataClient.getBoardByID(addedBoardId.value);
|
||
|
||
if (!boardInfo) {
|
||
throw new Error("无法获取板卡信息");
|
||
}
|
||
|
||
// 更新主机IP和主机MAC
|
||
await netConfigClient.updateHostIP();
|
||
await netConfigClient.updateHostMAC();
|
||
|
||
// 设置板卡IP和MAC
|
||
await netConfigClient.setBoardIP(boardInfo.ipAddr);
|
||
await netConfigClient.setBoardMAC(boardInfo.macAddr);
|
||
|
||
// 更新板卡状态为可用
|
||
if (
|
||
(await dataClient.updateBoardStatus(
|
||
boardInfo.id,
|
||
BoardStatus.Available,
|
||
)) != 1
|
||
) {
|
||
throw new Error("无法更新板卡状态");
|
||
}
|
||
|
||
if (!(await boardManager.getAllBoards()).success) {
|
||
alertStore?.error("无法获取板卡列表");
|
||
}
|
||
|
||
// 获取实验板网络信息
|
||
const networkInfo = await netConfigClient.getNetworkConfig();
|
||
|
||
if (networkInfo) {
|
||
networkConfig.value = networkInfo;
|
||
currentStep.value = "result";
|
||
alertStore?.success("实验板配置成功");
|
||
} else {
|
||
throw new Error("无法获取网络配置信息");
|
||
}
|
||
} catch (error) {
|
||
console.error("配置实验板失败:", error);
|
||
alertStore?.error("配置实验板失败");
|
||
|
||
// 配置失败,删除数据库中的板卡信息
|
||
try {
|
||
const dataClient = AuthManager.createClient(DataClient);
|
||
await dataClient.deleteBoard(addedBoardId.value);
|
||
} catch (deleteError) {
|
||
console.error("删除板卡失败:", deleteError);
|
||
}
|
||
|
||
// 返回输入步骤
|
||
currentStep.value = "input";
|
||
} finally {
|
||
isConfiguring.value = false;
|
||
}
|
||
}
|
||
|
||
// 处理成功
|
||
function handleSuccess() {
|
||
emit("success");
|
||
emit("update:visible", false);
|
||
resetForm();
|
||
}
|
||
|
||
// 监听对话框显示状态,重置表单
|
||
watch(
|
||
() => props.visible,
|
||
(newVisible) => {
|
||
if (newVisible) {
|
||
resetForm();
|
||
}
|
||
},
|
||
);
|
||
</script>
|
||
|
||
<style scoped lang="postcss">
|
||
@import "@/assets/main.css";
|
||
|
||
.form-control {
|
||
@apply w-full;
|
||
}
|
||
|
||
.label-text {
|
||
@apply font-medium;
|
||
}
|
||
|
||
.input-error {
|
||
@apply border-error;
|
||
}
|
||
|
||
.text-error {
|
||
@apply text-red-500;
|
||
}
|
||
|
||
.loading {
|
||
@apply opacity-50 cursor-not-allowed;
|
||
}
|
||
|
||
.alert {
|
||
@apply rounded-lg p-4;
|
||
}
|
||
|
||
.alert-info {
|
||
@apply bg-blue-50 text-blue-800 border border-blue-200;
|
||
}
|
||
|
||
.alert-warning {
|
||
@apply bg-yellow-50 text-yellow-800 border border-yellow-200;
|
||
}
|
||
|
||
.alert-success {
|
||
@apply bg-green-50 text-green-800 border border-green-200;
|
||
}
|
||
</style>
|