feat: 后端添加获取空闲实验板,继续修改前端界面使其更加合理

This commit is contained in:
SikongJueluo 2025-07-12 14:59:28 +08:00
parent 44e357b887
commit 0fb0c4e395
No known key found for this signature in database
11 changed files with 222 additions and 117 deletions

View File

@ -1,5 +1,4 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Authorization; 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>
/// 新增板子(管理员权限) /// 新增板子(管理员权限)
/// </summary> /// </summary>

View File

@ -29,35 +29,35 @@ public class User
public required string EMail { get; set; } public required string EMail { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// 用户的密码(应该进行哈希处理)
/// </summary> /// </summary>
[NotNull] [NotNull]
public required string Password { get; set; } public required string Password { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// 用户权限等级
/// </summary> /// </summary>
[NotNull] [NotNull]
public required UserPermission Permission { get; set; } public required UserPermission Permission { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// 绑定的实验板ID如果未绑定则为空
/// </summary> /// </summary>
[Nullable] [Nullable]
public Guid BoardID { get; set; } public Guid BoardID { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// 用户权限枚举
/// </summary> /// </summary>
public enum UserPermission public enum UserPermission
{ {
/// <summary> /// <summary>
/// [TODO:description] /// 管理员权限,可以管理用户和实验板
/// </summary> /// </summary>
Admin, Admin,
/// <summary> /// <summary>
/// [TODO:description] /// 普通用户权限,只能使用实验板
/// </summary> /// </summary>
Normal, Normal,
} }
@ -81,41 +81,41 @@ public class Board
public required string BoardName { get; set; } public required string BoardName { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// FPGA 板子的IP地址
/// </summary> /// </summary>
[NotNull] [NotNull]
public required string IpAddr { get; set; } public required string IpAddr { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// FPGA 板子的通信端口
/// </summary> /// </summary>
[NotNull] [NotNull]
public required int Port { get; set; } public required int Port { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// FPGA 板子的当前状态
/// </summary> /// </summary>
[NotNull] [NotNull]
public required BoardStatus Status { get; set; } public required BoardStatus Status { get; set; }
/// <summary> /// <summary>
/// [TODO:description] /// FPGA 板子的固件版本号
/// </summary> /// </summary>
[NotNull] [NotNull]
public string FirmVersion { get; set; } = "1.0.0"; public string FirmVersion { get; set; } = "1.0.0";
/// <summary> /// <summary>
/// [TODO:description] /// FPGA 板子状态枚举
/// </summary> /// </summary>
public enum BoardStatus public enum BoardStatus
{ {
/// <summary> /// <summary>
/// [TODO:description] /// 繁忙状态,正在被用户使用
/// </summary> /// </summary>
Busy, Busy,
/// <summary> /// <summary>
/// [TODO:description] /// 可用状态,可以被分配给用户
/// </summary> /// </summary>
Available, Available,
} }
@ -140,6 +140,7 @@ public class AppDataConnection : DataConnection
{ {
if (!Path.Exists(DATABASE_FILEPATH)) if (!Path.Exists(DATABASE_FILEPATH))
{ {
logger.Info($"数据库文件不存在,正在创建新数据库: {DATABASE_FILEPATH}");
LinqToDB.DataProvider.SQLite.SQLiteTools.CreateDatabase(DATABASE_FILEPATH); LinqToDB.DataProvider.SQLite.SQLiteTools.CreateDatabase(DATABASE_FILEPATH);
this.CreateAllTables(); this.CreateAllTables();
var user = new User() var user = new User()
@ -150,6 +151,11 @@ public class AppDataConnection : DataConnection
Permission = Database.User.UserPermission.Admin, Permission = Database.User.UserPermission.Admin,
}; };
this.Insert(user); this.Insert(user);
logger.Info("默认管理员用户已创建");
}
else
{
logger.Info($"数据库连接已建立: {DATABASE_FILEPATH}");
} }
} }
@ -159,8 +165,10 @@ public class AppDataConnection : DataConnection
/// </summary> /// </summary>
public void CreateAllTables() public void CreateAllTables()
{ {
logger.Info("正在创建数据库表...");
this.CreateTable<User>(); this.CreateTable<User>();
this.CreateTable<Board>(); this.CreateTable<Board>();
logger.Info("数据库表创建完成");
} }
/// <summary> /// <summary>
@ -168,16 +176,18 @@ public class AppDataConnection : DataConnection
/// </summary> /// </summary>
public void DropAllTables() public void DropAllTables()
{ {
logger.Warn("正在删除所有数据库表...");
this.DropTable<User>(); this.DropTable<User>();
this.DropTable<Board>(); this.DropTable<Board>();
logger.Warn("所有数据库表已删除");
} }
/// <summary> /// <summary>
/// 添加一个新的用户到数据库 /// 添加一个新的用户到数据库
/// </summary> /// </summary>
/// <param name="name">用户的名称</param> /// <param name="name">用户的名称</param>
/// <param name="email">[TODO:parameter]</param> /// <param name="email">用户的电子邮箱地址</param>
/// <param name="password">[TODO:parameter]</param> /// <param name="password">用户的密码</param>
/// <returns>插入的记录数</returns> /// <returns>插入的记录数</returns>
public int AddUser(string name, string email, string password) public int AddUser(string name, string email, string password)
{ {
@ -188,63 +198,67 @@ public class AppDataConnection : DataConnection
Password = password, Password = password,
Permission = Database.User.UserPermission.Normal, Permission = Database.User.UserPermission.Normal,
}; };
return this.Insert(user); var result = this.Insert(user);
logger.Info($"新用户已添加: {name} ({email})");
return result;
} }
/// <summary> /// <summary>
/// [TODO:description] /// 根据用户名获取用户信息
/// </summary> /// </summary>
/// <param name="name">[TODO:parameter]</param> /// <param name="name">用户名</param>
/// <returns>[TODO:return]</returns> /// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
public Result<Optional<User>> GetUserByName(string name) public Result<Optional<User>> GetUserByName(string name)
{ {
var user = this.User.Where((user) => user.Name == name).ToArray(); var user = this.User.Where((user) => user.Name == name).ToArray();
if (user.Length > 1) if (user.Length > 1)
{ {
logger.Error($"TODO"); logger.Error($"数据库中存在多个同名用户: {name}");
return new(new Exception($"TODO")); return new(new Exception($"数据库中存在多个同名用户: {name}"));
} }
if (user.Length == 0) if (user.Length == 0)
{ {
logger.Info($"TODO"); logger.Info($"未找到用户: {name}");
return new(Optional<User>.None); return new(Optional<User>.None);
} }
logger.Debug($"成功获取用户信息: {name}");
return new(user[0]); return new(user[0]);
} }
/// <summary> /// <summary>
/// [TODO:description] /// 根据电子邮箱获取用户信息
/// </summary> /// </summary>
/// <param name="email">[TODO:parameter]</param> /// <param name="email">用户的电子邮箱地址</param>
/// <returns>[TODO:return]</returns> /// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
public Result<Optional<User>> GetUserByEMail(string email) public Result<Optional<User>> GetUserByEMail(string email)
{ {
var user = this.User.Where((user) => user.EMail == email).ToArray(); var user = this.User.Where((user) => user.EMail == email).ToArray();
if (user.Length > 1) if (user.Length > 1)
{ {
logger.Error($"TODO"); logger.Error($"数据库中存在多个相同邮箱的用户: {email}");
return new(new Exception($"TODO")); return new(new Exception($"数据库中存在多个相同邮箱的用户: {email}"));
} }
if (user.Length == 0) if (user.Length == 0)
{ {
logger.Info($"TODO"); logger.Info($"未找到邮箱对应的用户: {email}");
return new(Optional<User>.None); return new(Optional<User>.None);
} }
logger.Debug($"成功获取用户信息: {email}");
return new(user[0]); return new(user[0]);
} }
/// <summary> /// <summary>
/// [TODO:description] /// 验证用户密码
/// </summary> /// </summary>
/// <param name="name">[TODO:parameter]</param> /// <param name="name">用户名</param>
/// <param name="password">[TODO:parameter]</param> /// <param name="password">用户密码</param>
/// <returns>[TODO:return]</returns> /// <returns>如果密码正确返回用户信息,否则返回空</returns>
public Result<Optional<User>> CheckUserPassword(string name, string password) public Result<Optional<User>> CheckUserPassword(string name, string password)
{ {
var ret = this.GetUserByName(name); var ret = this.GetUserByName(name);
@ -256,16 +270,40 @@ public class AppDataConnection : DataConnection
var user = ret.Value.Value; var user = ret.Value.Value;
if (user.Password == password) return new(user); if (user.Password == password)
else return new(Optional<User>.None); {
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> /// <summary>
/// 添加一块新的 FPGA 板子到数据库 /// 添加一块新的 FPGA 板子到数据库
/// </summary> /// </summary>
/// <param name="name">FPGA 板子的名称</param> /// <param name="name">FPGA 板子的名称</param>
/// <param name="ipAddr">[TODO:Param]</param> /// <param name="ipAddr">FPGA 板子的IP地址</param>
/// <param name="port">[TODO:Param]</param> /// <param name="port">FPGA 板子的通信端口</param>
/// <returns>插入的记录数</returns> /// <returns>插入的记录数</returns>
public int AddBoard(string name, string ipAddr, int port) public int AddBoard(string name, string ipAddr, int port)
{ {
@ -276,51 +314,59 @@ public class AppDataConnection : DataConnection
Port = port, Port = port,
Status = Database.Board.BoardStatus.Available, Status = Database.Board.BoardStatus.Available,
}; };
return this.Insert(board); var result = this.Insert(board);
logger.Info($"新实验板已添加: {name} ({ipAddr}:{port})");
return result;
} }
/// <summary> /// <summary>
/// [TODO:description] /// 根据名称删除实验板
/// </summary> /// </summary>
/// <param name="name">[TODO:parameter]</param> /// <param name="name">实验板的名称</param>
/// <returns>[TODO:return]</returns> /// <returns>删除的记录数</returns>
public int DeleteBoardByName(string name) 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> /// <summary>
/// [TODO:description] /// 根据ID删除实验板
/// </summary> /// </summary>
/// <param name="id">[TODO:parameter]</param> /// <param name="id">实验板的唯一标识符</param>
/// <returns>[TODO:return]</returns> /// <returns>删除的记录数</returns>
public int DeleteBoardByID(Guid id) 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> /// <summary>
/// [TODO:description] /// 获取所有实验板信息
/// </summary> /// </summary>
/// <returns>[TODO:return]</returns> /// <returns>所有实验板的数组</returns>
public Board[] GetAllBoard() public Board[] GetAllBoard()
{ {
return this.Board.ToArray(); var boards = this.Board.ToArray();
logger.Debug($"获取所有实验板,共 {boards.Length} 块");
return boards;
} }
/// <summary> /// <summary>
/// [TODO:description] /// 获取一块可用的实验板并将其状态设置为繁忙
/// </summary> /// </summary>
/// <returns>[TODO:return]</returns> /// <returns>可用的实验板,如果没有可用的板子则返回空</returns>
public Optional<Board> GetAvailableBoard() public Optional<Board> GetAvailableBoard()
{ {
var boards = this.Board.Where( var boards = this.Board.Where(
(board) => board.Status == Database.Board.BoardStatus.Available (board) => board.Status == Database.Board.BoardStatus.Available
).ToArray(); ).ToArray();
if (boards.Length < 0) if (boards.Length == 0)
{ {
logger.Warn($"TODO"); logger.Warn("没有可用的实验板");
return new(null); return new(null);
} }
else else
@ -331,6 +377,7 @@ public class AppDataConnection : DataConnection
.Where(target => target.ID == board.ID) .Where(target => target.ID == board.ID)
.Set(target => target.Status, board.Status) .Set(target => target.Status, board.Status)
.Update(); .Update();
logger.Info($"实验板 {board.BoardName} ({board.ID}) 已分配,状态更新为繁忙");
return new(board); return new(board);
} }
} }

View File

@ -649,6 +649,54 @@ export class DataClient {
return Promise.resolve<boolean>(null as any); 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) * @param name (optional)
@ -2721,13 +2769,13 @@ export class Board implements IBoard {
id!: string; id!: string;
/** FPGA 板子的名称 */ /** FPGA 板子的名称 */
boardName!: string; boardName!: string;
/** [TODO:description] */ /** FPGA 板子的IP地址 */
ipAddr!: string; ipAddr!: string;
/** [TODO:description] */ /** FPGA 板子的通信端口 */
port!: number; port!: number;
/** [TODO:description] */ /** FPGA 板子的当前状态 */
status!: BoardStatus; status!: BoardStatus;
/** [TODO:description] */ /** FPGA 板子的固件版本号 */
firmVersion!: string; firmVersion!: string;
constructor(data?: IBoard) { constructor(data?: IBoard) {
@ -2775,17 +2823,17 @@ export interface IBoard {
id: string; id: string;
/** FPGA 板子的名称 */ /** FPGA 板子的名称 */
boardName: string; boardName: string;
/** [TODO:description] */ /** FPGA 板子的IP地址 */
ipAddr: string; ipAddr: string;
/** [TODO:description] */ /** FPGA 板子的通信端口 */
port: number; port: number;
/** [TODO:description] */ /** FPGA 板子的当前状态 */
status: BoardStatus; status: BoardStatus;
/** [TODO:description] */ /** FPGA 板子的固件版本号 */
firmVersion: string; firmVersion: string;
} }
/** [TODO:description] */ /** FPGA 板子状态枚举 */
export enum BoardStatus { export enum BoardStatus {
Busy = 0, Busy = 0,
Available = 1, Available = 1,

View File

@ -34,55 +34,9 @@
to="/project" 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" 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 <BookOpen class="h-5 w-5 mr-2" />
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>
进入工程界面 进入工程界面
</router-link> </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>
<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" 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> <script lang="ts" setup>
import "@/router"; import "@/router";
import TutorialCarousel from "@/components/TutorialCarousel.vue"; import TutorialCarousel from "@/components/TutorialCarousel.vue";
import { BookOpen } from "lucide-vue-next";
</script> </script>
<style scoped lang="postcss"> <style scoped lang="postcss">

View File

@ -72,7 +72,7 @@
:min-size="15" :min-size="15"
class="w-full overflow-hidden" class="w-full overflow-hidden"
> >
<FunctionBar class="mx-4 mt-1" /> <BottomBar class="mx-4 mt-1" />
</SplitterPanel> </SplitterPanel>
</SplitterGroup> </SplitterGroup>
</div> </div>
@ -89,18 +89,21 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch } from "vue"; import { ref, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from "reka-ui"; import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from "reka-ui";
import DiagramCanvas from "@/components/LabCanvas/DiagramCanvas.vue"; import DiagramCanvas from "@/components/LabCanvas/DiagramCanvas.vue";
import ComponentSelector from "@/components/LabCanvas/ComponentSelector.vue"; import ComponentSelector from "@/components/LabCanvas/ComponentSelector.vue";
import PropertyPanel from "@/components/PropertyPanel.vue"; import PropertyPanel from "@/components/PropertyPanel.vue";
import MarkdownRenderer from "@/components/MarkdownRenderer.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 { useProvideComponentManager } from "@/components/LabCanvas";
import type { DiagramData } from "@/components/LabCanvas"; import type { DiagramData } from "@/components/LabCanvas";
import { useAlertStore } from "@/components/Alert"; import { useAlertStore } from "@/components/Alert";
import { AuthManager } from "@/utils/AuthManager";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
const route = useRoute(); const route = useRoute();
const router = useRouter();
// //
const componentManager = useProvideComponentManager(); const componentManager = useProvideComponentManager();
@ -207,6 +210,20 @@ function updateComponentDirectProp(
// --- --- // --- ---
onMounted(async () => { 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) { if (route.query.tutorial) {
showDocPanel.value = true; showDocPanel.value = true;

View File

@ -1,6 +1,6 @@
<template> <template>
<div <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"> <div class="card bg-base-200 shadow-xl">

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="min-h-screen bg-base-100"> <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 bg-base-200 shadow-xl">
<div class="card-body"> <div class="card-body">

View File

@ -91,7 +91,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch } from 'vue'; import { ref, reactive, watch } from 'vue';
import { useBoardManager } from './BoardManager'; import { useBoardManager } from '../../utils/BoardManager';
// Props Emits // Props Emits
interface Props { interface Props {

View File

@ -202,7 +202,7 @@
import { FlexRender } from "@tanstack/vue-table"; import { FlexRender } from "@tanstack/vue-table";
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import { RefreshCw, Edit, Plus, Trash2 } from "lucide-vue-next"; import { RefreshCw, Edit, Plus, Trash2 } from "lucide-vue-next";
import { useProvideBoardManager } from "./BoardManager"; import { useProvideBoardManager } from "../../utils/BoardManager";
import { useProvideBoardTableManager } from "./BoardTableManager"; import { useProvideBoardTableManager } from "./BoardTableManager";
import AddBoardDialog from "./AddBoardDialog.vue"; import AddBoardDialog from "./AddBoardDialog.vue";

View File

@ -15,8 +15,8 @@ import {
} from "@tanstack/vue-table"; } from "@tanstack/vue-table";
import { h, ref, computed, version } from "vue"; import { h, ref, computed, version } from "vue";
import { createInjectionState } from "@vueuse/core"; import { createInjectionState } from "@vueuse/core";
import type { BoardData } from "./BoardManager"; import type { BoardData } from "../../utils/BoardManager";
import { useBoardManager } from "./BoardManager"; import { useBoardManager } from "../../utils/BoardManager";
import { useDialogStore } from "@/stores/dialog"; import { useDialogStore } from "@/stores/dialog";
const [useProvideBoardTableManager, useBoardTableManager] = const [useProvideBoardTableManager, useBoardTableManager] =