feat: 后端添加获取空闲实验板,继续修改前端界面使其更加合理
This commit is contained in:
@@ -34,55 +34,9 @@
|
||||
to="/project"
|
||||
class="btn btn-primary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 mr-2"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
|
||||
</svg>
|
||||
<BookOpen class="h-5 w-5 mr-2" />
|
||||
进入工程界面
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
to="/login"
|
||||
class="btn btn-secondary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 mr-2"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
登录
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
to="/user"
|
||||
class="btn btn-accent text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 mr-2"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
用户中心
|
||||
</router-link>
|
||||
</div>
|
||||
<div
|
||||
class="mt-8 p-4 bg-base-300 rounded-lg shadow-inner opacity-80 transition-all duration-300 hover:opacity-100 hover:shadow-md"
|
||||
@@ -101,6 +55,7 @@
|
||||
<script lang="ts" setup>
|
||||
import "@/router";
|
||||
import TutorialCarousel from "@/components/TutorialCarousel.vue";
|
||||
import { BookOpen } from "lucide-vue-next";
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
:min-size="15"
|
||||
class="w-full overflow-hidden"
|
||||
>
|
||||
<FunctionBar class="mx-4 mt-1" />
|
||||
<BottomBar class="mx-4 mt-1" />
|
||||
</SplitterPanel>
|
||||
</SplitterGroup>
|
||||
</div>
|
||||
@@ -89,18 +89,21 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from "reka-ui";
|
||||
import DiagramCanvas from "@/components/LabCanvas/DiagramCanvas.vue";
|
||||
import ComponentSelector from "@/components/LabCanvas/ComponentSelector.vue";
|
||||
import PropertyPanel from "@/components/PropertyPanel.vue";
|
||||
import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
|
||||
import FunctionBar from "@/views/Project/BottomBar.vue";
|
||||
import BottomBar from "@/views/Project/BottomBar.vue";
|
||||
import { useProvideComponentManager } from "@/components/LabCanvas";
|
||||
import type { DiagramData } from "@/components/LabCanvas";
|
||||
import { useAlertStore } from "@/components/Alert";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
|
||||
import { useRoute } from "vue-router";
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 提供组件管理服务
|
||||
const componentManager = useProvideComponentManager();
|
||||
@@ -207,6 +210,20 @@ function updateComponentDirectProp(
|
||||
|
||||
// --- 生命周期钩子 ---
|
||||
onMounted(async () => {
|
||||
// 验证用户身份
|
||||
try {
|
||||
const isAuthenticated = await AuthManager.isAuthenticated();
|
||||
if (!isAuthenticated) {
|
||||
// 验证失败,跳转到登录页面
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('身份验证失败:', error);
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有例程参数,如果有则自动打开文档面板
|
||||
if (route.query.tutorial) {
|
||||
showDocPanel.value = true;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="min-h-screen bg-base-100 flex flex-col mx-auto p-6 space-y-6 container"
|
||||
class="min-h-screen bg-base-100 flex flex-col p-6 space-y-6 "
|
||||
>
|
||||
<!-- 设置 -->
|
||||
<div class="card bg-base-200 shadow-xl">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-base-100">
|
||||
<div class="container mx-auto p-6 space-y-6">
|
||||
<div class="p-6">
|
||||
<!-- 控制面板 -->
|
||||
<div class="card bg-base-200 shadow-xl">
|
||||
<div class="card-body">
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { useBoardManager } from './BoardManager';
|
||||
import { useBoardManager } from '../../utils/BoardManager';
|
||||
|
||||
// Props 和 Emits
|
||||
interface Props {
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
import { ref } from "vue";
|
||||
import { createInjectionState } from "@vueuse/core";
|
||||
import { RemoteUpdateClient, DataClient, Board } from "@/APIClient";
|
||||
import { Common } from "@/utils/Common";
|
||||
import { isUndefined } from "lodash";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
|
||||
// 统一的板卡数据接口,扩展原有的Board类型
|
||||
export interface BoardData extends Board {
|
||||
defaultBitstream: string;
|
||||
goldBitstreamFile?: File;
|
||||
appBitstream1File?: File;
|
||||
appBitstream2File?: File;
|
||||
appBitstream3File?: File;
|
||||
}
|
||||
|
||||
const [useProvideBoardManager, useBoardManager] = createInjectionState(() => {
|
||||
// 远程升级相关参数
|
||||
const devPort = 1234;
|
||||
const remoteUpdater = new RemoteUpdateClient();
|
||||
|
||||
// 统一的板卡数据
|
||||
const boards = ref<BoardData[]>([]);
|
||||
|
||||
// 获取位流编号
|
||||
function getSelectedBitstreamNum(bitstreamName: string): number {
|
||||
if (bitstreamName === "黄金位流") return 0;
|
||||
if (bitstreamName === "应用位流1") return 1;
|
||||
if (bitstreamName === "应用位流2") return 2;
|
||||
if (bitstreamName === "应用位流3") return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取所有板卡信息(管理员权限)
|
||||
async function getAllBoards(): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// 验证管理员权限
|
||||
const hasAdminAuth = await AuthManager.verifyAdminAuth();
|
||||
if (!hasAdminAuth) {
|
||||
console.error("权限验证失败");
|
||||
return { success: false, error: "权限不足" };
|
||||
}
|
||||
|
||||
const client = AuthManager.createAuthenticatedClient();
|
||||
const result = await client.getAllBoards();
|
||||
|
||||
if (result) {
|
||||
// 将Board类型转换为BoardData类型,添加默认值
|
||||
boards.value = result.map((board) => {
|
||||
return {
|
||||
...board,
|
||||
defaultBitstream: "黄金位流", // 设置默认位流
|
||||
goldBitstreamFile: undefined,
|
||||
appBitstream1File: undefined,
|
||||
appBitstream2File: undefined,
|
||||
appBitstream3File: undefined,
|
||||
// Ensure methods from Board are present
|
||||
init: board.init?.bind(board),
|
||||
toJSON: board.toJSON?.bind(board),
|
||||
};
|
||||
});
|
||||
console.log("获取板卡信息成功", result.length);
|
||||
return { success: true };
|
||||
} else {
|
||||
console.error("获取板卡信息失败:返回结果为空");
|
||||
return { success: false, error: "获取板卡信息失败" };
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("获取板卡信息异常:", e);
|
||||
return { success: false, error: e.message || "获取板卡信息异常" };
|
||||
}
|
||||
}
|
||||
|
||||
// 新增板卡(管理员权限)
|
||||
async function addBoard(
|
||||
name: string,
|
||||
ipAddr: string,
|
||||
port: number,
|
||||
): Promise<{ success: boolean; error?: string; boardId?: string }> {
|
||||
try {
|
||||
// 验证管理员权限
|
||||
const hasAdminAuth = await AuthManager.verifyAdminAuth();
|
||||
if (!hasAdminAuth) {
|
||||
console.error("权限验证失败");
|
||||
return { success: false, error: "权限不足" };
|
||||
}
|
||||
|
||||
// 验证输入参数
|
||||
if (!name || !ipAddr || !port) {
|
||||
console.error("参数验证失败", { name, ipAddr, port });
|
||||
return { success: false, error: "参数不完整" };
|
||||
}
|
||||
|
||||
const client = AuthManager.createAuthenticatedClient();
|
||||
const boardId = await client.addBoard(name, ipAddr, port);
|
||||
|
||||
if (boardId) {
|
||||
console.log("新增板卡成功", { boardId, name, ipAddr, port });
|
||||
// 刷新板卡列表
|
||||
await getAllBoards();
|
||||
return { success: true};
|
||||
} else {
|
||||
console.error("新增板卡失败:返回ID为空");
|
||||
return { success: false, error: "新增板卡失败" };
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("新增板卡异常:", e);
|
||||
if (e.status === 401) {
|
||||
return { success: false, error: "权限不足" };
|
||||
} else if (e.status === 400) {
|
||||
return { success: false, error: "输入参数错误" };
|
||||
} else {
|
||||
return { success: false, error: e.message || "新增板卡异常" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除板卡(管理员权限)
|
||||
async function deleteBoard(boardId: string): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// 验证管理员权限
|
||||
const hasAdminAuth = await AuthManager.verifyAdminAuth();
|
||||
if (!hasAdminAuth) {
|
||||
console.error("权限验证失败");
|
||||
return { success: false, error: "权限不足" };
|
||||
}
|
||||
|
||||
if (!boardId) {
|
||||
console.error("板卡ID为空");
|
||||
return { success: false, error: "板卡ID不能为空" };
|
||||
}
|
||||
|
||||
const client = AuthManager.createAuthenticatedClient();
|
||||
const result = await client.deleteBoard(boardId);
|
||||
|
||||
if (result > 0) {
|
||||
console.log("删除板卡成功", { boardId, deletedCount: result });
|
||||
// 刷新板卡列表
|
||||
await getAllBoards();
|
||||
return { success: true };
|
||||
} else {
|
||||
console.error("删除板卡失败:影响行数为0");
|
||||
return { success: false, error: "删除板卡失败" };
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("删除板卡异常:", e);
|
||||
if (e.status === 401) {
|
||||
return { success: false, error: "权限不足" };
|
||||
} else if (e.status === 400) {
|
||||
return { success: false, error: "输入参数错误" };
|
||||
} else {
|
||||
return { success: false, error: e.message || "删除板卡异常" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 上传并固化位流
|
||||
async function uploadAndDownloadBitstreams(
|
||||
board: BoardData,
|
||||
goldBitstream?: File,
|
||||
appBitstream1?: File,
|
||||
appBitstream2?: File,
|
||||
appBitstream3?: File,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
let cnt = 0;
|
||||
if (!isUndefined(goldBitstream)) cnt++;
|
||||
if (!isUndefined(appBitstream1)) cnt++;
|
||||
if (!isUndefined(appBitstream2)) cnt++;
|
||||
if (!isUndefined(appBitstream3)) cnt++;
|
||||
|
||||
if (cnt === 0) {
|
||||
console.error("未选择比特流文件");
|
||||
return { success: false, error: "未选择比特流文件" };
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("开始上传比特流", { boardIp: board.ipAddr, fileCount: cnt });
|
||||
|
||||
const uploadResult = await remoteUpdater.uploadBitstreams(
|
||||
board.ipAddr,
|
||||
Common.toFileParameterOrNull(goldBitstream),
|
||||
Common.toFileParameterOrNull(appBitstream1),
|
||||
Common.toFileParameterOrNull(appBitstream2),
|
||||
Common.toFileParameterOrNull(appBitstream3),
|
||||
);
|
||||
|
||||
if (!uploadResult) {
|
||||
console.error("上传比特流失败");
|
||||
return { success: false, error: "上传比特流失败" };
|
||||
}
|
||||
|
||||
console.log("比特流上传成功,开始固化");
|
||||
|
||||
const downloadResult = await remoteUpdater.downloadMultiBitstreams(
|
||||
board.ipAddr,
|
||||
board.port,
|
||||
getSelectedBitstreamNum(board.defaultBitstream),
|
||||
);
|
||||
|
||||
if (downloadResult != cnt) {
|
||||
console.error("固化比特流失败", { expected: cnt, actual: downloadResult });
|
||||
return { success: false, error: "固化比特流失败" };
|
||||
} else {
|
||||
console.log("固化比特流成功", { count: downloadResult });
|
||||
return { success: true };
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("比特流操作异常:", e);
|
||||
return { success: false, error: e.message || "比特流操作异常" };
|
||||
}
|
||||
}
|
||||
|
||||
// 热启动位流
|
||||
async function hotresetBitstream(
|
||||
board: BoardData,
|
||||
bitstreamNum: number
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
console.log("开始热启动比特流", { boardIp: board.ipAddr, bitstreamNum });
|
||||
|
||||
const ret = await remoteUpdater.hotResetBitstream(
|
||||
board.ipAddr,
|
||||
board.port,
|
||||
bitstreamNum,
|
||||
);
|
||||
|
||||
if (ret) {
|
||||
console.log("热启动比特流成功");
|
||||
return { success: true };
|
||||
} else {
|
||||
console.error("热启动比特流失败");
|
||||
return { success: false, error: "热启动比特流失败" };
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("热启动比特流异常:", e);
|
||||
return { success: false, error: e.message || "热启动比特流异常" };
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件上传
|
||||
function handleFileChange(
|
||||
event: Event,
|
||||
board: BoardData,
|
||||
fileKey: keyof Pick<
|
||||
BoardData,
|
||||
| "goldBitstreamFile"
|
||||
| "appBitstream1File"
|
||||
| "appBitstream2File"
|
||||
| "appBitstream3File"
|
||||
>,
|
||||
) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
if (file) {
|
||||
(board as any)[fileKey] = file;
|
||||
console.log(`文件选择成功`, { boardIp: board.ipAddr, fileKey, fileName: file.name });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
boards,
|
||||
uploadAndDownloadBitstreams,
|
||||
hotresetBitstream,
|
||||
handleFileChange,
|
||||
getSelectedBitstreamNum,
|
||||
getAllBoards,
|
||||
addBoard,
|
||||
deleteBoard,
|
||||
};
|
||||
});
|
||||
|
||||
export { useProvideBoardManager, useBoardManager };
|
||||
@@ -202,7 +202,7 @@
|
||||
import { FlexRender } from "@tanstack/vue-table";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { RefreshCw, Edit, Plus, Trash2 } from "lucide-vue-next";
|
||||
import { useProvideBoardManager } from "./BoardManager";
|
||||
import { useProvideBoardManager } from "../../utils/BoardManager";
|
||||
import { useProvideBoardTableManager } from "./BoardTableManager";
|
||||
import AddBoardDialog from "./AddBoardDialog.vue";
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
} from "@tanstack/vue-table";
|
||||
import { h, ref, computed, version } from "vue";
|
||||
import { createInjectionState } from "@vueuse/core";
|
||||
import type { BoardData } from "./BoardManager";
|
||||
import { useBoardManager } from "./BoardManager";
|
||||
import type { BoardData } from "../../utils/BoardManager";
|
||||
import { useBoardManager } from "../../utils/BoardManager";
|
||||
import { useDialogStore } from "@/stores/dialog";
|
||||
|
||||
const [useProvideBoardTableManager, useBoardTableManager] =
|
||||
|
||||
Reference in New Issue
Block a user