refactor: 使用更简洁的方式进行认证

This commit is contained in:
2025-08-16 14:53:28 +08:00
parent c974de593a
commit e61cf96c07
24 changed files with 2118 additions and 2089 deletions

View File

@@ -274,7 +274,7 @@ const handleSignUp = async () => {
// 页面初始化时检查是否已有有效token
const checkExistingToken = async () => {
try {
const isValid = await AuthManager.verifyToken();
const isValid = await AuthManager.isAuthenticated();
if (isValid) {
// 如果token仍然有效直接跳转到project页面
router.go(-1);

View File

@@ -418,7 +418,12 @@ import {
FileArchiveIcon,
FileJsonIcon,
} from "lucide-vue-next";
import { ExamDto, type FileParameter } from "@/APIClient";
import {
ExamClient,
ExamDto,
ResourceClient,
type FileParameter,
} from "@/APIClient";
import { useAlertStore } from "@/components/Alert";
import { AuthManager } from "@/utils/AuthManager";
import { useRequiredInjection } from "@/utils/Common";
@@ -618,7 +623,7 @@ const submitCreateExam = async () => {
isUpdating.value = true;
try {
const client = AuthManager.createAuthenticatedExamClient();
const client = AuthManager.createClient(ExamClient);
let exam: ExamInfo;
if (mode.value === "create") {
@@ -671,7 +676,7 @@ const submitCreateExam = async () => {
// 上传实验资源
async function uploadExamResources(examId: string) {
const client = AuthManager.createAuthenticatedResourceClient();
const client = AuthManager.createClient(ResourceClient);
try {
// 上传MD文档
@@ -750,7 +755,7 @@ function close() {
}
async function editExam(examId: string) {
const client = AuthManager.createAuthenticatedExamClient();
const client = AuthManager.createClient(ExamClient);
const examInfo = await client.getExam(examId);
editExamInfo.value = {

View File

@@ -250,7 +250,13 @@
</div>
</template>
<script setup lang="ts">
import { ResourcePurpose, type ExamInfo, type ResourceInfo } from "@/APIClient";
import {
ExamClient,
ResourceClient,
ResourcePurpose,
type ExamInfo,
type ResourceInfo,
} from "@/APIClient";
import { useAlertStore } from "@/components/Alert";
import { AuthManager } from "@/utils/AuthManager";
import { useRequiredInjection } from "@/utils/Common";
@@ -274,7 +280,7 @@ const props = defineProps<{
const commitsList = ref<ResourceInfo[]>();
async function updateCommits() {
const client = AuthManager.createAuthenticatedExamClient();
const client = AuthManager.createClient(ExamClient);
const list = await client.getCommitsByExamId(props.selectedExam.id);
commitsList.value = list;
}
@@ -288,7 +294,7 @@ const downloadResources = async () => {
downloadingResources.value = true;
try {
const resourceClient = AuthManager.createAuthenticatedResourceClient();
const resourceClient = AuthManager.createClient(ResourceClient);
// 获取资源包列表(模板资源)
const resourceList = await resourceClient.getResourceList(

View File

@@ -181,7 +181,7 @@
import { ref, onMounted, computed } from "vue";
import { useRoute } from "vue-router";
import { AuthManager } from "@/utils/AuthManager";
import { type ExamInfo } from "@/APIClient";
import { ExamClient, type ExamInfo } from "@/APIClient";
import { formatDate } from "@/utils/Common";
import ExamInfoModal from "./ExamInfoModal.vue";
import ExamEditModal from "./ExamEditModal.vue";
@@ -206,7 +206,7 @@ async function refreshExams() {
error.value = "";
try {
const client = AuthManager.createAuthenticatedExamClient();
const client = AuthManager.createClient(ExamClient);
exams.value = await client.getExamList();
} catch (err: any) {
error.value = err.message || "获取实验列表失败";
@@ -218,7 +218,7 @@ async function refreshExams() {
async function viewExam(examId: string) {
try {
const client = AuthManager.createAuthenticatedExamClient();
const client = AuthManager.createClient(ExamClient);
selectedExam.value = await client.getExam(examId);
showInfoModal.value = true;
} catch (err: any) {
@@ -248,7 +248,7 @@ onMounted(async () => {
router.push("/login");
}
isAdmin.value = await AuthManager.verifyAdminAuth();
isAdmin.value = await AuthManager.isAdminAuthenticated();
await refreshExams();

View File

@@ -266,7 +266,12 @@
</template>
<script setup lang="ts">
import { CaptureMode, ChannelConfig, DebuggerConfig } from "@/APIClient";
import {
CaptureMode,
ChannelConfig,
DebuggerClient,
DebuggerConfig,
} from "@/APIClient";
import { useAlertStore } from "@/components/Alert";
import BaseInputField from "@/components/InputField/BaseInputField.vue";
import type { LogicDataType } from "@/components/WaveformDisplay";
@@ -421,7 +426,7 @@ async function startCapture() {
}
isCapturing.value = true;
const client = AuthManager.createAuthenticatedDebuggerClient();
const client = AuthManager.createClient(DebuggerClient);
// 构造API配置
const channelConfigs = channels.value

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
@layout="handleVerticalSplitterResize"
>
<!-- 使用 v-show 替代 v-if -->
<SplitterPanel
<SplitterPanel
v-show="!isBottomBarFullscreen"
id="splitter-group-v-panel-project"
:default-size="verticalSplitterSize"
@@ -60,8 +60,8 @@
v-show="showDocPanel"
class="doc-panel overflow-y-auto h-full"
>
<MarkdownRenderer
:content="documentContent"
<MarkdownRenderer
:content="documentContent"
:examId="(route.query.examId as string) || ''"
/>
</div>
@@ -80,11 +80,13 @@
<!-- 功能底栏 -->
<SplitterPanel
id="splitter-group-v-panel-bar"
:default-size="isBottomBarFullscreen ? 100 : (100 - verticalSplitterSize)"
:default-size="
isBottomBarFullscreen ? 100 : 100 - verticalSplitterSize
"
:min-size="isBottomBarFullscreen ? 100 : 15"
class="w-full overflow-hidden pt-3"
>
<BottomBar
<BottomBar
:isFullscreen="isBottomBarFullscreen"
@toggle-fullscreen="handleToggleBottomBarFullscreen"
/>
@@ -106,22 +108,48 @@
/>
<!-- Navbar切换浮动按钮 -->
<div
<div
class="navbar-toggle-btn"
:class="{ 'with-navbar': navbarControl.showNavbar.value }"
>
<button
<button
@click="navbarControl.toggleNavbar"
class="btn btn-circle btn-primary shadow-lg hover:shadow-xl transition-all duration-300"
:class="{ 'btn-outline': navbarControl.showNavbar.value }"
:title="navbarControl.showNavbar.value ? '隐藏顶部导航栏' : '显示顶部导航栏'"
:title="
navbarControl.showNavbar.value ? '隐藏顶部导航栏' : '显示顶部导航栏'
"
>
<!-- 使用SVG图标表示菜单/关闭状态 -->
<svg v-if="navbarControl.showNavbar.value" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
<svg
v-if="navbarControl.showNavbar.value"
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
<svg
v-else
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</button>
</div>
@@ -131,7 +159,7 @@
<script setup lang="ts">
import { ref, onMounted, watch, inject, type Ref } from "vue";
import { useRouter } from "vue-router";
import { useLocalStorage } from '@vueuse/core'; // 添加VueUse导入
import { useLocalStorage } from "@vueuse/core"; // 添加VueUse导入
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from "reka-ui";
import DiagramCanvas from "@/components/LabCanvas/DiagramCanvas.vue";
import ComponentSelector from "@/components/LabCanvas/ComponentSelector.vue";
@@ -143,7 +171,7 @@ import { useProvideComponentManager } from "@/components/LabCanvas";
import { useAlertStore } from "@/components/Alert";
import { AuthManager } from "@/utils/AuthManager";
import { useEquipments } from "@/stores/equipments";
import type { Board } from "@/APIClient";
import { DataClient, ResourceClient, type Board } from "@/APIClient";
import { useRoute } from "vue-router";
const route = useRoute();
@@ -158,20 +186,29 @@ const equipments = useEquipments();
const alert = useAlertStore();
// --- Navbar控制 ---
const navbarControl = inject('navbar') as {
const navbarControl = inject("navbar") as {
showNavbar: Ref<boolean>;
toggleNavbar: () => void;
};
// --- 使用VueUse保存分栏状态 ---
// 左右分栏比例默认60%
const horizontalSplitterSize = useLocalStorage('project-horizontal-splitter-size', 60);
const horizontalSplitterSize = useLocalStorage(
"project-horizontal-splitter-size",
60,
);
// 上下分栏比例默认80%
const verticalSplitterSize = useLocalStorage('project-vertical-splitter-size', 80);
const verticalSplitterSize = useLocalStorage(
"project-vertical-splitter-size",
80,
);
// 底栏全屏状态
const isBottomBarFullscreen = useLocalStorage('project-bottom-bar-fullscreen', false);
const isBottomBarFullscreen = useLocalStorage(
"project-bottom-bar-fullscreen",
false,
);
// 文档面板显示状态
const showDocPanel = useLocalStorage('project-show-doc-panel', false);
const showDocPanel = useLocalStorage("project-show-doc-panel", false);
function handleToggleBottomBarFullscreen() {
isBottomBarFullscreen.value = !isBottomBarFullscreen.value;
@@ -216,25 +253,25 @@ async function loadDocumentContent() {
const examId = route.query.examId as string;
if (examId) {
// 如果有实验ID从API加载实验文档
console.log('加载实验文档:', examId);
const client = AuthManager.createAuthenticatedResourceClient();
console.log("加载实验文档:", examId);
const client = AuthManager.createClient(ResourceClient);
// 获取markdown类型的模板资源列表
const resources = await client.getResourceList(examId, 'doc', 'template');
const resources = await client.getResourceList(examId, "doc", "template");
if (resources && resources.length > 0) {
// 获取第一个markdown资源
const markdownResource = resources[0];
// 使用新的ResourceClient API获取资源文件内容
const response = await client.getResourceById(markdownResource.id);
if (!response || !response.data) {
throw new Error('获取markdown文件失败');
throw new Error("获取markdown文件失败");
}
const content = await response.data.text();
// 更新文档内容暂时不处理图片路径由MarkdownRenderer处理
documentContent.value = content;
} else {
@@ -279,17 +316,17 @@ function updateComponentDirectProp(
// 检查并初始化用户实验板
async function checkAndInitializeBoard() {
try {
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
const userInfo = await client.getUserInfo();
if (userInfo.boardID && userInfo.boardID.trim() !== '') {
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);
console.error("获取实验板信息失败:", boardError);
alert?.show("获取实验板信息失败", "error");
showRequestBoardDialog.value = true;
}
@@ -298,7 +335,7 @@ async function checkAndInitializeBoard() {
showRequestBoardDialog.value = true;
}
} catch (error) {
console.error('检查用户实验板失败:', error);
console.error("检查用户实验板失败:", error);
alert?.show("检查用户信息失败", "error");
showRequestBoardDialog.value = true;
}
@@ -308,12 +345,12 @@ async function checkAndInitializeBoard() {
function updateEquipmentFromBoard(board: Board) {
equipments.boardAddr = board.ipAddr;
equipments.boardPort = board.port;
console.log(`实验板信息已更新到equipment store:`, {
address: board.ipAddr,
port: board.port,
boardName: board.boardName,
boardId: board.id
boardId: board.id,
});
}
@@ -321,7 +358,7 @@ function updateEquipmentFromBoard(board: Board) {
function handleRequestBoardClose() {
showRequestBoardDialog.value = false;
// 如果用户取消申请,可以选择返回上一页或显示警告
router.push('/');
router.push("/");
}
// 处理申请实验板成功
@@ -338,12 +375,12 @@ onMounted(async () => {
const isAuthenticated = await AuthManager.isAuthenticated();
if (!isAuthenticated) {
// 验证失败,跳转到登录页面
router.push('/login');
router.push("/login");
return;
}
} catch (error) {
console.error('身份验证失败:', error);
router.push('/login');
console.error("身份验证失败:", error);
router.push("/login");
return;
}

View File

@@ -75,7 +75,7 @@ import { ref, watch } from "vue";
import { CheckCircle } from "lucide-vue-next";
import { AuthManager } from "@/utils/AuthManager";
import { useAlertStore } from "@/components/Alert";
import type { Board } from "@/APIClient";
import { DataClient, type Board } from "@/APIClient";
interface Props {
open: boolean;
@@ -113,7 +113,7 @@ async function checkUserBoard() {
boardInfo.value = null;
try {
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
const userInfo = await client.getUserInfo();
if (userInfo.boardID && userInfo.boardID.trim() !== "") {
@@ -140,7 +140,7 @@ async function requestBoard() {
requesting.value = true;
try {
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
const board = await client.getAvailableBoard(undefined);
if (board) {

View File

@@ -433,7 +433,7 @@ const currentVideoSource = ref("");
const logs = ref<Array<{ time: Date; level: string; message: string }>>([]);
// API 客户端
const videoClient = AuthManager.createAuthenticatedVideoStreamClient();
const videoClient = AuthManager.createClient(VideoStreamClient);
// 添加日志
const addLog = (level: string, message: string) => {

View File

@@ -174,7 +174,12 @@
import { ref, reactive, watch } from "vue";
import { AuthManager } from "../../utils/AuthManager";
import { useAlertStore } from "../../components/Alert";
import { BoardStatus, type NetworkConfigDto } from "../../APIClient";
import {
BoardStatus,
DataClient,
NetConfigClient,
type NetworkConfigDto,
} from "../../APIClient";
import { useRequiredInjection } from "@/utils/Common";
import { useBoardManager } from "@/utils/BoardManager";
@@ -267,8 +272,7 @@ async function handleSubmit() {
isSubmitting.value = true;
try {
// 通过 AuthManager 获取认证的 DataClient
const dataClient = AuthManager.createAuthenticatedDataClient();
const dataClient = AuthManager.createClient(DataClient);
// 添加板卡到数据库
const boardId = await dataClient.addBoard(form.name.trim());
@@ -293,8 +297,7 @@ async function handleCancelPairing() {
if (!addedBoardId.value) return;
try {
// 通过 AuthManager 获取认证的 DataClient
const dataClient = AuthManager.createAuthenticatedDataClient();
const dataClient = AuthManager.createClient(DataClient);
// 删除添加的板卡
await dataClient.deleteBoard(addedBoardId.value);
@@ -317,8 +320,8 @@ async function handlePairingConfirm() {
try {
// 通过 AuthManager 获取认证的客户端
const dataClient = AuthManager.createAuthenticatedDataClient();
const netConfigClient = AuthManager.createAuthenticatedNetConfigClient();
const dataClient = AuthManager.createClient(DataClient);
const netConfigClient = AuthManager.createClient(NetConfigClient);
// 获取数据库中对应分配的板卡信息
const boardInfo = await dataClient.getBoardByID(addedBoardId.value);
@@ -365,7 +368,7 @@ async function handlePairingConfirm() {
// 配置失败,删除数据库中的板卡信息
try {
const dataClient = AuthManager.createAuthenticatedDataClient();
const dataClient = AuthManager.createClient(DataClient);
await dataClient.deleteBoard(addedBoardId.value);
} catch (deleteError) {
console.error("删除板卡失败:", deleteError);

View File

@@ -42,12 +42,12 @@ const isAdmin = ref(false);
function setActivePage(event: Event) {
const target = event.currentTarget as HTMLLinkElement;
const newPage = toNumber(target.id);
// 如果用户不是管理员但试图访问管理员页面,则忽略
if (newPage === 100 && !isAdmin.value) {
return;
}
activePage.value = newPage;
}
@@ -60,16 +60,16 @@ onMounted(async () => {
// 这里可以使用路由跳转
return;
}
// 验证管理员权限
isAdmin.value = await AuthManager.verifyAdminAuth();
isAdmin.value = await AuthManager.isAdminAuthenticated();
// 如果当前页面是管理员页面但用户不是管理员,切换到用户信息页面
if (activePage.value === 100 && !isAdmin.value) {
activePage.value = 1;
}
} catch (error) {
console.error('用户认证检查失败:', error);
console.error("用户认证检查失败:", error);
// 可以在这里处理错误,比如显示错误信息或重定向到登录页面
}
});

View File

@@ -273,7 +273,13 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { AuthManager } from "@/utils/AuthManager";
import { UserInfo, Board, BoardStatus } from "@/APIClient";
import {
UserInfo,
Board,
BoardStatus,
DataClient,
JtagClient,
} from "@/APIClient";
import { Alert, useAlertStore } from "@/components/Alert";
import {
User,
@@ -319,7 +325,7 @@ const loadBoardInfo = async () => {
}
try {
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
boardInfo.value = await client.getBoardByID(userInfo.value.boardID);
} catch (err) {
console.error("加载实验板信息失败:", err);
@@ -335,7 +341,7 @@ const loadUserInfo = async (showSuccessMessage = false) => {
try {
await new Promise((resolve) => setTimeout(resolve, 200));
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
userInfo.value = await client.getUserInfo();
// 如果有绑定的实验板ID加载实验板信息
@@ -370,7 +376,7 @@ const applyBoard = async () => {
alertStore?.info("正在申请实验板...");
try {
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
// 获取可用的实验板
const availableBoard = await client.getAvailableBoard(undefined);
@@ -407,7 +413,7 @@ const testBoardConnection = async () => {
alertStore?.info("正在测试连接...");
try {
const jtagClient = AuthManager.createAuthenticatedJtagClient();
const jtagClient = AuthManager.createClient(JtagClient);
// 使用JTAG客户端读取设备ID Code
const idCode = await jtagClient.getDeviceIDCode(
@@ -444,7 +450,7 @@ const unbindBoard = async () => {
alertStore?.info("正在解绑实验板...");
try {
const client = AuthManager.createAuthenticatedDataClient();
const client = AuthManager.createClient(DataClient);
const success = await client.unbindBoard();
if (success) {