feat: 后端添加获取空闲实验板,继续修改前端界面使其更加合理
This commit is contained in:
		@@ -1,5 +1,4 @@
 | 
			
		||||
using System.IdentityModel.Tokens.Jwt;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
@@ -192,6 +191,45 @@ public class DataController : ControllerBase
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取一个空闲的实验板(普通用户权限)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Authorize]
 | 
			
		||||
    [HttpGet("GetAvailableBoard")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public IActionResult GetAvailableBoard()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            using var db = new Database.AppDataConnection();
 | 
			
		||||
            var boardOpt = db.GetAvailableBoard();
 | 
			
		||||
            if (!boardOpt.HasValue)
 | 
			
		||||
                return NotFound("没有可用的实验板");
 | 
			
		||||
 | 
			
		||||
            // 绑定用户与实验板
 | 
			
		||||
            var userName = User.Identity?.Name;
 | 
			
		||||
            if (string.IsNullOrEmpty(userName))
 | 
			
		||||
                return Unauthorized("未找到用户名信息");
 | 
			
		||||
 | 
			
		||||
            var userRet = db.GetUserByName(userName);
 | 
			
		||||
            if (!userRet.IsSuccessful || !userRet.Value.HasValue)
 | 
			
		||||
                return BadRequest("用户不存在");
 | 
			
		||||
 | 
			
		||||
            var user = userRet.Value.Value;
 | 
			
		||||
            db.BindUserToBoard(user.ID, boardOpt.Value.ID);
 | 
			
		||||
 | 
			
		||||
            return Ok(boardOpt.Value);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "获取空闲实验板时发生异常");
 | 
			
		||||
            return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 新增板子(管理员权限)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -29,35 +29,35 @@ public class User
 | 
			
		||||
    public required string EMail { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 用户的密码(应该进行哈希处理)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    public required string Password { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 用户权限等级
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    public required UserPermission Permission { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 绑定的实验板ID,如果未绑定则为空
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Nullable]
 | 
			
		||||
    public Guid BoardID { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 用户权限枚举
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public enum UserPermission
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// [TODO:description]
 | 
			
		||||
        /// 管理员权限,可以管理用户和实验板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Admin,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// [TODO:description]
 | 
			
		||||
        /// 普通用户权限,只能使用实验板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Normal,
 | 
			
		||||
    }
 | 
			
		||||
@@ -81,41 +81,41 @@ public class Board
 | 
			
		||||
    public required string BoardName { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// FPGA 板子的IP地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    public required string IpAddr { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// FPGA 板子的通信端口
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    public required int Port { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// FPGA 板子的当前状态
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    public required BoardStatus Status { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// FPGA 板子的固件版本号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    public string FirmVersion { get; set; } = "1.0.0";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// FPGA 板子状态枚举
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public enum BoardStatus
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// [TODO:description]
 | 
			
		||||
        /// 繁忙状态,正在被用户使用
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Busy,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// [TODO:description]
 | 
			
		||||
        /// 可用状态,可以被分配给用户
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Available,
 | 
			
		||||
    }
 | 
			
		||||
@@ -140,6 +140,7 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
    {
 | 
			
		||||
        if (!Path.Exists(DATABASE_FILEPATH))
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"数据库文件不存在,正在创建新数据库: {DATABASE_FILEPATH}");
 | 
			
		||||
            LinqToDB.DataProvider.SQLite.SQLiteTools.CreateDatabase(DATABASE_FILEPATH);
 | 
			
		||||
            this.CreateAllTables();
 | 
			
		||||
            var user = new User()
 | 
			
		||||
@@ -150,6 +151,11 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
                Permission = Database.User.UserPermission.Admin,
 | 
			
		||||
            };
 | 
			
		||||
            this.Insert(user);
 | 
			
		||||
            logger.Info("默认管理员用户已创建");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"数据库连接已建立: {DATABASE_FILEPATH}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -159,8 +165,10 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void CreateAllTables()
 | 
			
		||||
    {
 | 
			
		||||
        logger.Info("正在创建数据库表...");
 | 
			
		||||
        this.CreateTable<User>();
 | 
			
		||||
        this.CreateTable<Board>();
 | 
			
		||||
        logger.Info("数据库表创建完成");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -168,16 +176,18 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void DropAllTables()
 | 
			
		||||
    {
 | 
			
		||||
        logger.Warn("正在删除所有数据库表...");
 | 
			
		||||
        this.DropTable<User>();
 | 
			
		||||
        this.DropTable<Board>();
 | 
			
		||||
        logger.Warn("所有数据库表已删除");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加一个新的用户到数据库
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">用户的名称</param>
 | 
			
		||||
    /// <param name="email">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="password">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="email">用户的电子邮箱地址</param>
 | 
			
		||||
    /// <param name="password">用户的密码</param>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    public int AddUser(string name, string email, string password)
 | 
			
		||||
    {
 | 
			
		||||
@@ -188,63 +198,67 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
            Password = password,
 | 
			
		||||
            Permission = Database.User.UserPermission.Normal,
 | 
			
		||||
        };
 | 
			
		||||
        return this.Insert(user);
 | 
			
		||||
        var result = this.Insert(user);
 | 
			
		||||
        logger.Info($"新用户已添加: {name} ({email})");
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 根据用户名获取用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <param name="name">用户名</param>
 | 
			
		||||
    /// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
 | 
			
		||||
    public Result<Optional<User>> GetUserByName(string name)
 | 
			
		||||
    {
 | 
			
		||||
        var user = this.User.Where((user) => user.Name == name).ToArray();
 | 
			
		||||
 | 
			
		||||
        if (user.Length > 1)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"TODO");
 | 
			
		||||
            return new(new Exception($"TODO"));
 | 
			
		||||
            logger.Error($"数据库中存在多个同名用户: {name}");
 | 
			
		||||
            return new(new Exception($"数据库中存在多个同名用户: {name}"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (user.Length == 0)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"TODO");
 | 
			
		||||
            logger.Info($"未找到用户: {name}");
 | 
			
		||||
            return new(Optional<User>.None);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.Debug($"成功获取用户信息: {name}");
 | 
			
		||||
        return new(user[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 根据电子邮箱获取用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="email">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <param name="email">用户的电子邮箱地址</param>
 | 
			
		||||
    /// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
 | 
			
		||||
    public Result<Optional<User>> GetUserByEMail(string email)
 | 
			
		||||
    {
 | 
			
		||||
        var user = this.User.Where((user) => user.EMail == email).ToArray();
 | 
			
		||||
 | 
			
		||||
        if (user.Length > 1)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"TODO");
 | 
			
		||||
            return new(new Exception($"TODO"));
 | 
			
		||||
            logger.Error($"数据库中存在多个相同邮箱的用户: {email}");
 | 
			
		||||
            return new(new Exception($"数据库中存在多个相同邮箱的用户: {email}"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (user.Length == 0)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"TODO");
 | 
			
		||||
            logger.Info($"未找到邮箱对应的用户: {email}");
 | 
			
		||||
            return new(Optional<User>.None);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.Debug($"成功获取用户信息: {email}");
 | 
			
		||||
        return new(user[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 验证用户密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="password">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <param name="name">用户名</param>
 | 
			
		||||
    /// <param name="password">用户密码</param>
 | 
			
		||||
    /// <returns>如果密码正确返回用户信息,否则返回空</returns>
 | 
			
		||||
    public Result<Optional<User>> CheckUserPassword(string name, string password)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = this.GetUserByName(name);
 | 
			
		||||
@@ -256,16 +270,40 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
 | 
			
		||||
        var user = ret.Value.Value;
 | 
			
		||||
 | 
			
		||||
        if (user.Password == password) return new(user);
 | 
			
		||||
        else return new(Optional<User>.None);
 | 
			
		||||
        if (user.Password == password) 
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"用户 {name} 密码验证成功");
 | 
			
		||||
            return new(user);
 | 
			
		||||
        }
 | 
			
		||||
        else 
 | 
			
		||||
        {
 | 
			
		||||
            logger.Warn($"用户 {name} 密码验证失败");
 | 
			
		||||
            return new(Optional<User>.None);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 绑定用户与实验板
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId">用户的唯一标识符</param>
 | 
			
		||||
    /// <param name="boardId">实验板的唯一标识符</param>
 | 
			
		||||
    /// <returns>更新的记录数</returns>
 | 
			
		||||
    public int BindUserToBoard(Guid userId, Guid boardId)
 | 
			
		||||
    {
 | 
			
		||||
        var result = this.User
 | 
			
		||||
            .Where(u => u.ID == userId)
 | 
			
		||||
            .Set(u => u.BoardID, boardId)
 | 
			
		||||
            .Update();
 | 
			
		||||
        logger.Info($"用户 {userId} 已绑定到实验板 {boardId}");
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加一块新的 FPGA 板子到数据库
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">FPGA 板子的名称</param>
 | 
			
		||||
    /// <param name="ipAddr">[TODO:Param]</param>
 | 
			
		||||
    /// <param name="port">[TODO:Param]</param>
 | 
			
		||||
    /// <param name="ipAddr">FPGA 板子的IP地址</param>
 | 
			
		||||
    /// <param name="port">FPGA 板子的通信端口</param>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    public int AddBoard(string name, string ipAddr, int port)
 | 
			
		||||
    {
 | 
			
		||||
@@ -276,51 +314,59 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
            Port = port,
 | 
			
		||||
            Status = Database.Board.BoardStatus.Available,
 | 
			
		||||
        };
 | 
			
		||||
        return this.Insert(board);
 | 
			
		||||
        var result = this.Insert(board);
 | 
			
		||||
        logger.Info($"新实验板已添加: {name} ({ipAddr}:{port})");
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 根据名称删除实验板
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <param name="name">实验板的名称</param>
 | 
			
		||||
    /// <returns>删除的记录数</returns>
 | 
			
		||||
    public int DeleteBoardByName(string name)
 | 
			
		||||
    {
 | 
			
		||||
        return this.Board.Where(board => board.BoardName == name).Delete();
 | 
			
		||||
        var result = this.Board.Where(board => board.BoardName == name).Delete();
 | 
			
		||||
        logger.Info($"实验板已删除: {name},删除记录数: {result}");
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 根据ID删除实验板
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="id">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <param name="id">实验板的唯一标识符</param>
 | 
			
		||||
    /// <returns>删除的记录数</returns>
 | 
			
		||||
    public int DeleteBoardByID(Guid id)
 | 
			
		||||
    {
 | 
			
		||||
        return this.Board.Where(board => board.ID == id).Delete();
 | 
			
		||||
        var result = this.Board.Where(board => board.ID == id).Delete();
 | 
			
		||||
        logger.Info($"实验板已删除: {id},删除记录数: {result}");
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 获取所有实验板信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <returns>所有实验板的数组</returns>
 | 
			
		||||
    public Board[] GetAllBoard()
 | 
			
		||||
    {
 | 
			
		||||
        return this.Board.ToArray();
 | 
			
		||||
        var boards = this.Board.ToArray();
 | 
			
		||||
        logger.Debug($"获取所有实验板,共 {boards.Length} 块");
 | 
			
		||||
        return boards;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 获取一块可用的实验板并将其状态设置为繁忙
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <returns>可用的实验板,如果没有可用的板子则返回空</returns>
 | 
			
		||||
    public Optional<Board> GetAvailableBoard()
 | 
			
		||||
    {
 | 
			
		||||
        var boards = this.Board.Where(
 | 
			
		||||
                (board) => board.Status == Database.Board.BoardStatus.Available
 | 
			
		||||
            ).ToArray();
 | 
			
		||||
 | 
			
		||||
        if (boards.Length < 0)
 | 
			
		||||
        if (boards.Length == 0)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Warn($"TODO");
 | 
			
		||||
            logger.Warn("没有可用的实验板");
 | 
			
		||||
            return new(null);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
@@ -331,6 +377,7 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
                .Where(target => target.ID == board.ID)
 | 
			
		||||
                .Set(target => target.Status, board.Status)
 | 
			
		||||
                .Update();
 | 
			
		||||
            logger.Info($"实验板 {board.BoardName} ({board.ID}) 已分配,状态更新为繁忙");
 | 
			
		||||
            return new(board);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -649,6 +649,54 @@ export class DataClient {
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取一个空闲的实验板(普通用户权限)
 | 
			
		||||
     */
 | 
			
		||||
    getAvailableBoard(): Promise<Board> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/Data/GetAvailableBoard";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "GET",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processGetAvailableBoard(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processGetAvailableBoard(response: Response): Promise<Board> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result200 = Board.fromJS(resultData200);
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 404) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result404: any = null;
 | 
			
		||||
            let resultData404 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result404 = ProblemDetails.fromJS(resultData404);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result404);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<Board>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 新增板子(管理员权限)
 | 
			
		||||
     * @param name (optional) 
 | 
			
		||||
@@ -2721,13 +2769,13 @@ export class Board implements IBoard {
 | 
			
		||||
    id!: string;
 | 
			
		||||
    /** FPGA 板子的名称 */
 | 
			
		||||
    boardName!: string;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的IP地址 */
 | 
			
		||||
    ipAddr!: string;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的通信端口 */
 | 
			
		||||
    port!: number;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的当前状态 */
 | 
			
		||||
    status!: BoardStatus;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的固件版本号 */
 | 
			
		||||
    firmVersion!: string;
 | 
			
		||||
 | 
			
		||||
    constructor(data?: IBoard) {
 | 
			
		||||
@@ -2775,17 +2823,17 @@ export interface IBoard {
 | 
			
		||||
    id: string;
 | 
			
		||||
    /** FPGA 板子的名称 */
 | 
			
		||||
    boardName: string;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的IP地址 */
 | 
			
		||||
    ipAddr: string;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的通信端口 */
 | 
			
		||||
    port: number;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的当前状态 */
 | 
			
		||||
    status: BoardStatus;
 | 
			
		||||
    /** [TODO:description] */
 | 
			
		||||
    /** FPGA 板子的固件版本号 */
 | 
			
		||||
    firmVersion: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** [TODO:description] */
 | 
			
		||||
/** FPGA 板子状态枚举 */
 | 
			
		||||
export enum BoardStatus {
 | 
			
		||||
    Busy = 0,
 | 
			
		||||
    Available = 1,
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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