feat: 完善用户界面,添加绑定与解除绑定的功能
This commit is contained in:
@@ -84,6 +84,13 @@
|
||||
@add-template="handleAddTemplate"
|
||||
@close="showComponentsMenu = false"
|
||||
/>
|
||||
|
||||
<!-- 实验板申请对话框 -->
|
||||
<RequestBoardDialog
|
||||
:open="showRequestBoardDialog"
|
||||
@close="handleRequestBoardClose"
|
||||
@success="handleRequestBoardSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -96,10 +103,13 @@ import ComponentSelector from "@/components/LabCanvas/ComponentSelector.vue";
|
||||
import PropertyPanel from "@/components/PropertyPanel.vue";
|
||||
import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
|
||||
import BottomBar from "@/views/Project/BottomBar.vue";
|
||||
import RequestBoardDialog from "@/views/Project/RequestBoardDialog.vue";
|
||||
import { useProvideComponentManager } from "@/components/LabCanvas";
|
||||
import type { DiagramData } from "@/components/LabCanvas";
|
||||
import { useAlertStore } from "@/components/Alert";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
import { useEquipments } from "@/stores/equipments";
|
||||
import type { Board } from "@/APIClient";
|
||||
|
||||
import { useRoute } from "vue-router";
|
||||
const route = useRoute();
|
||||
@@ -108,8 +118,14 @@ const router = useRouter();
|
||||
// 提供组件管理服务
|
||||
const componentManager = useProvideComponentManager();
|
||||
|
||||
// 设备管理store
|
||||
const equipments = useEquipments();
|
||||
|
||||
const alert = useAlertStore();
|
||||
|
||||
// --- 实验板申请对话框 ---
|
||||
const showRequestBoardDialog = ref(false);
|
||||
|
||||
// --- 文档面板控制 ---
|
||||
const showDocPanel = ref(false);
|
||||
const documentContent = ref("");
|
||||
@@ -208,6 +224,62 @@ function updateComponentDirectProp(
|
||||
componentManager.updateComponentDirectProp(componentId, propName, value);
|
||||
}
|
||||
|
||||
// --- 实验板管理 ---
|
||||
// 检查并初始化用户实验板
|
||||
async function checkAndInitializeBoard() {
|
||||
try {
|
||||
const client = AuthManager.createAuthenticatedDataClient();
|
||||
const userInfo = await client.getUserInfo();
|
||||
|
||||
if (userInfo.boardID && userInfo.boardID.trim() !== '') {
|
||||
// 用户已绑定实验板,获取实验板信息并更新到equipment
|
||||
try {
|
||||
const board = await client.getBoardByID(userInfo.boardID);
|
||||
updateEquipmentFromBoard(board);
|
||||
alert?.show(`实验板 ${board.boardName} 已连接`, "success");
|
||||
} catch (boardError) {
|
||||
console.error('获取实验板信息失败:', boardError);
|
||||
alert?.show("获取实验板信息失败", "error");
|
||||
showRequestBoardDialog.value = true;
|
||||
}
|
||||
} else {
|
||||
// 用户未绑定实验板,显示申请对话框
|
||||
showRequestBoardDialog.value = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查用户实验板失败:', error);
|
||||
alert?.show("检查用户信息失败", "error");
|
||||
showRequestBoardDialog.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据实验板信息更新equipment store
|
||||
function updateEquipmentFromBoard(board: Board) {
|
||||
equipments.setAddr(board.ipAddr);
|
||||
equipments.setPort(board.port);
|
||||
|
||||
console.log(`实验板信息已更新到equipment store:`, {
|
||||
address: board.ipAddr,
|
||||
port: board.port,
|
||||
boardName: board.boardName,
|
||||
boardId: board.id
|
||||
});
|
||||
}
|
||||
|
||||
// 处理申请实验板对话框关闭
|
||||
function handleRequestBoardClose() {
|
||||
showRequestBoardDialog.value = false;
|
||||
// 如果用户取消申请,可以选择返回上一页或显示警告
|
||||
router.push('/');
|
||||
}
|
||||
|
||||
// 处理申请实验板成功
|
||||
function handleRequestBoardSuccess(board: Board) {
|
||||
showRequestBoardDialog.value = false;
|
||||
updateEquipmentFromBoard(board);
|
||||
alert?.show(`实验板 ${board.boardName} 申请成功!`, "success");
|
||||
}
|
||||
|
||||
// --- 生命周期钩子 ---
|
||||
onMounted(async () => {
|
||||
// 验证用户身份
|
||||
@@ -224,6 +296,9 @@ onMounted(async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查并初始化用户实验板
|
||||
await checkAndInitializeBoard();
|
||||
|
||||
// 检查是否有例程参数,如果有则自动打开文档面板
|
||||
if (route.query.tutorial) {
|
||||
showDocPanel.value = true;
|
||||
|
||||
179
src/views/Project/RequestBoardDialog.vue
Normal file
179
src/views/Project/RequestBoardDialog.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="open"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
|
||||
>
|
||||
<div class="bg-base-100 rounded-lg shadow-xl max-w-md w-full mx-4">
|
||||
<div class="p-6">
|
||||
<h2 class="text-xl font-bold mb-4">申请实验板</h2>
|
||||
|
||||
<div v-if="!loading && !hasBoard" class="space-y-4">
|
||||
<p class="text-base-content">
|
||||
检测到您尚未绑定实验板,请申请一个可用的实验板以继续实验。
|
||||
</p>
|
||||
|
||||
<div class="flex justify-end space-x-2">
|
||||
<button
|
||||
@click="$emit('close')"
|
||||
class="btn btn-ghost"
|
||||
:disabled="requesting"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
@click="requestBoard"
|
||||
class="btn btn-primary"
|
||||
:disabled="requesting"
|
||||
>
|
||||
<span
|
||||
v-if="requesting"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ requesting ? "申请中..." : "申请实验板" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="loading" class="text-center py-8">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
<p class="mt-2">检查实验板状态中...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="hasBoard" class="space-y-4">
|
||||
<div class="alert alert-success">
|
||||
<CheckCircle class="shrink-0 h-6 w-6" />
|
||||
<span>实验板绑定成功!</span>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-200 p-4 rounded">
|
||||
<h3 class="font-semibold mb-2">实验板信息</h3>
|
||||
<div class="space-y-1 text-sm">
|
||||
<p>
|
||||
<span class="font-medium">名称:</span>
|
||||
{{ boardInfo?.boardName }}
|
||||
</p>
|
||||
<p><span class="font-medium">ID:</span> {{ boardInfo?.id }}</p>
|
||||
<p>
|
||||
<span class="font-medium">地址:</span>
|
||||
{{ boardInfo?.ipAddr }}:{{ boardInfo?.port }}
|
||||
</p>
|
||||
<p>
|
||||
<span class="font-medium">状态:</span>
|
||||
<span class="badge badge-success">{{
|
||||
boardInfo?.status === 1 ? "可用" : "忙碌"
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
v-if="boardInfo"
|
||||
@click="$emit('success', boardInfo)"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
开始实验
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="alert alert-error mt-4">
|
||||
<XCircle class="shrink-0 h-6 w-6" />
|
||||
<span>{{ error }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { CheckCircle, XCircle } from "lucide-vue-next";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
import type { Board } from "@/APIClient";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: "close"): void;
|
||||
(e: "success", board: Board): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const loading = ref(false);
|
||||
const requesting = ref(false);
|
||||
const hasBoard = ref(false);
|
||||
const boardInfo = ref<Board | null>(null);
|
||||
const error = ref<string>("");
|
||||
|
||||
// 监听对话框打开状态,自动检查用户信息
|
||||
watch(
|
||||
() => props.open,
|
||||
async (newOpen) => {
|
||||
if (newOpen) {
|
||||
await checkUserBoard();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 检查用户是否已绑定实验板
|
||||
async function checkUserBoard() {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
hasBoard.value = false;
|
||||
boardInfo.value = null;
|
||||
|
||||
try {
|
||||
const client = AuthManager.createAuthenticatedDataClient();
|
||||
const userInfo = await client.getUserInfo();
|
||||
|
||||
if (userInfo.boardID && userInfo.boardID.trim() !== "") {
|
||||
// 用户已绑定实验板,获取实验板信息
|
||||
try {
|
||||
const board = await client.getBoardByID(userInfo.boardID);
|
||||
boardInfo.value = board;
|
||||
hasBoard.value = true;
|
||||
} catch (boardError) {
|
||||
console.error("获取实验板信息失败:", boardError);
|
||||
error.value = "获取实验板信息失败,请重试";
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("检查用户信息失败:", err);
|
||||
error.value = "检查用户信息失败,请重试";
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 申请实验板
|
||||
async function requestBoard() {
|
||||
requesting.value = true;
|
||||
error.value = "";
|
||||
|
||||
try {
|
||||
const client = AuthManager.createAuthenticatedDataClient();
|
||||
const board = await client.getAvailableBoard();
|
||||
|
||||
if (board) {
|
||||
boardInfo.value = board;
|
||||
hasBoard.value = true;
|
||||
} else {
|
||||
error.value = "当前没有可用的实验板,请稍后重试";
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("申请实验板失败:", err);
|
||||
if (err.status === 404) {
|
||||
error.value = "当前没有可用的实验板,请稍后重试";
|
||||
} else {
|
||||
error.value = "申请实验板失败,请重试";
|
||||
}
|
||||
} finally {
|
||||
requesting.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user