Compare commits
No commits in common. "d6167ac2864e9813098496b83a0f939dcb5bedc3" and "540f5c788d328c95f2cce5af4cd27edb1061345d" have entirely different histories.
d6167ac286
...
540f5c788d
|
@ -1,12 +1,9 @@
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NLog.Web;
|
using NLog.Web;
|
||||||
using NSwag.Generation.Processors.Security;
|
|
||||||
using server.Services;
|
using server.Services;
|
||||||
|
|
||||||
// Early init of NLog to allow startup and exception logging, before host is built
|
// Early init of NLog to allow startup and exception logging, before host is built
|
||||||
|
@ -41,27 +38,6 @@ try
|
||||||
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add JWT Token Authorization
|
|
||||||
builder.Services
|
|
||||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
||||||
.AddJwtBearer(options =>
|
|
||||||
{
|
|
||||||
options.TokenValidationParameters = new TokenValidationParameters
|
|
||||||
{
|
|
||||||
ValidateIssuer = true,
|
|
||||||
ValidateAudience = true,
|
|
||||||
ValidateLifetime = true,
|
|
||||||
ValidateIssuerSigningKey = true,
|
|
||||||
RequireExpirationTime = true,
|
|
||||||
ValidIssuer = "dlut.edu.cn",
|
|
||||||
ValidAudience = "dlut.edu.cn",
|
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(
|
|
||||||
Encoding.UTF8.GetBytes("my secret key 1234567890my secret key 1234567890")),
|
|
||||||
};
|
|
||||||
options.Authority = "http://localhost:5000";
|
|
||||||
options.RequireHttpsMetadata = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add CORS policy
|
// Add CORS policy
|
||||||
if (builder.Environment.IsDevelopment())
|
if (builder.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
@ -83,7 +59,7 @@ try
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add Swagger
|
// Add Swagger
|
||||||
builder.Services.AddSwaggerDocument(options =>
|
builder.Services.AddOpenApiDocument(options =>
|
||||||
{
|
{
|
||||||
options.PostProcess = document =>
|
options.PostProcess = document =>
|
||||||
{
|
{
|
||||||
|
@ -105,19 +81,8 @@ try
|
||||||
// }
|
// }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Authorization
|
|
||||||
options.AddSecurity("Bearer", new NSwag.OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Description = "请输入token,格式为 Bearer xxxxxxxx(注意中间必须有空格)",
|
|
||||||
Name = "Authorization",
|
|
||||||
In = NSwag.OpenApiSecurityApiKeyLocation.Header,
|
|
||||||
Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
|
|
||||||
});
|
|
||||||
options.OperationProcessors.Add(new OperationSecurityScopeProcessor("Bearer"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// 添加 HTTP 视频流服务
|
// 添加 HTTP 视频流服务
|
||||||
builder.Services.AddSingleton<HttpVideoStreamService>();
|
builder.Services.AddSingleton<HttpVideoStreamService>();
|
||||||
builder.Services.AddHostedService(provider => provider.GetRequiredService<HttpVideoStreamService>());
|
builder.Services.AddHostedService(provider => provider.GetRequiredService<HttpVideoStreamService>());
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
<PackageReference Include="DotNext.Threading" Version="5.19.1" />
|
<PackageReference Include="DotNext.Threading" Version="5.19.1" />
|
||||||
<PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" />
|
<PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" />
|
||||||
<PackageReference Include="linq2db.AspNet" Version="5.4.1" />
|
<PackageReference Include="linq2db.AspNet" Version="5.4.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="9.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="9.0.4" />
|
||||||
<PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />
|
<PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
|
|
||||||
namespace server.Controllers;
|
namespace server.Controllers;
|
||||||
|
|
||||||
|
@ -17,70 +13,57 @@ public class DataController : ControllerBase
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// [TODO:description]
|
/// 创建数据库表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">[TODO:parameter]</param>
|
/// <returns>插入的记录数</returns>
|
||||||
/// <param name="password">[TODO:parameter]</param>
|
[EnableCors("Development")]
|
||||||
/// <returns>[TODO:return]</returns>
|
[HttpPost("CreateTable")]
|
||||||
[HttpPost("login")]
|
public IResult CreateTables()
|
||||||
public IActionResult Login(string name, string password)
|
|
||||||
{
|
{
|
||||||
// 验证用户密码
|
|
||||||
using var db = new Database.AppDataConnection();
|
using var db = new Database.AppDataConnection();
|
||||||
var ret = db.CheckUserPassword(name, password);
|
db.CreateAllTables();
|
||||||
if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError);
|
return TypedResults.Ok();
|
||||||
if (!ret.Value.HasValue) return BadRequest($"TODO");
|
|
||||||
var user = ret.Value.Value;
|
|
||||||
|
|
||||||
// 生成 JWT
|
|
||||||
var tokenHandler = new JwtSecurityTokenHandler();
|
|
||||||
var key = Encoding.ASCII.GetBytes("my secret key 1234567890my secret key 1234567890");
|
|
||||||
var tokenDescriptor = new SecurityTokenDescriptor
|
|
||||||
{
|
|
||||||
Subject = new ClaimsIdentity(new Claim[]
|
|
||||||
{
|
|
||||||
new Claim(ClaimTypes.Name, user.Name),
|
|
||||||
new Claim(ClaimTypes.Email, user.EMail),
|
|
||||||
}),
|
|
||||||
Expires = DateTime.UtcNow.AddHours(1),
|
|
||||||
SigningCredentials = new SigningCredentials(
|
|
||||||
new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
|
|
||||||
Audience = "dlut.edu.cn",
|
|
||||||
Issuer = "dlut.edu.cn",
|
|
||||||
};
|
|
||||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
|
||||||
var jwt = tokenHandler.WriteToken(token);
|
|
||||||
|
|
||||||
return Ok(jwt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// [TODO:description]
|
/// 删除数据库表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
[HttpGet("TestAuth")]
|
[EnableCors("Development")]
|
||||||
[Authorize]
|
[HttpDelete("DropTables")]
|
||||||
public IActionResult TestAuth()
|
public IResult DropTables()
|
||||||
{
|
{
|
||||||
return Ok("Authenticated!");
|
using var db = new Database.AppDataConnection();
|
||||||
|
db.DropAllTables();
|
||||||
|
return TypedResults.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有用户
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>用户列表</returns>
|
||||||
|
[HttpGet("AllUsers")]
|
||||||
|
public IResult AllUsers()
|
||||||
|
{
|
||||||
|
using var db = new Database.AppDataConnection();
|
||||||
|
var ret = db.User.ToList();
|
||||||
|
return TypedResults.Ok(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册新用户
|
/// 注册新用户
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">用户名</param>
|
/// <param name="name">用户名</param>
|
||||||
/// <param name="email">[TODO:parameter]</param>
|
|
||||||
/// <param name="password">[TODO:parameter]</param>
|
|
||||||
/// <returns>操作结果</returns>
|
/// <returns>操作结果</returns>
|
||||||
[HttpPost("SignUpUser")]
|
[HttpPost("SignUpUser")]
|
||||||
public IActionResult SignUpUser(string name, string email, string password)
|
public IResult SignUpUser(string name)
|
||||||
{
|
{
|
||||||
if (name.Length > 255)
|
if (name.Length > 255)
|
||||||
return BadRequest("Name Couln't over 255 characters");
|
return TypedResults.BadRequest("Name Couln't over 255 characters");
|
||||||
|
|
||||||
using var db = new Database.AppDataConnection();
|
using var db = new Database.AppDataConnection();
|
||||||
var ret = db.AddUser(name, email, password);
|
var ret = db.AddUser(name);
|
||||||
return Ok(ret);
|
return TypedResults.Ok(ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using DotNext;
|
|
||||||
using LinqToDB;
|
using LinqToDB;
|
||||||
using LinqToDB.Data;
|
using LinqToDB.Data;
|
||||||
using LinqToDB.Mapping;
|
using LinqToDB.Mapping;
|
||||||
|
@ -21,46 +20,6 @@ public class User
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NotNull]
|
[NotNull]
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户的电子邮箱
|
|
||||||
/// </summary>
|
|
||||||
[NotNull]
|
|
||||||
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]
|
|
||||||
/// </summary>
|
|
||||||
[Nullable]
|
|
||||||
public Guid BoardID { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
public enum UserPermission
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
Admin,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
Normal,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -72,47 +31,13 @@ public class Board
|
||||||
/// FPGA 板子的唯一标识符
|
/// FPGA 板子的唯一标识符
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public Guid ID { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// FPGA 板子的名称
|
/// FPGA 板子的名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NotNull]
|
[NotNull]
|
||||||
public required string BoardName { get; set; }
|
public required string BoardName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
[NotNull]
|
|
||||||
public required string IpAddr { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
[NotNull]
|
|
||||||
public required int Port { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
[NotNull]
|
|
||||||
public required BoardStatus Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
public enum BoardStatus
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
Busy,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
Available,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -120,8 +45,6 @@ public class Board
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AppDataConnection : DataConnection
|
public class AppDataConnection : DataConnection
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
||||||
|
|
||||||
static readonly LinqToDB.DataOptions options =
|
static readonly LinqToDB.DataOptions options =
|
||||||
new LinqToDB.DataOptions()
|
new LinqToDB.DataOptions()
|
||||||
.UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite");
|
.UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite");
|
||||||
|
@ -154,87 +77,30 @@ public class AppDataConnection : DataConnection
|
||||||
/// 添加一个新的用户到数据库
|
/// 添加一个新的用户到数据库
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">用户的名称</param>
|
/// <param name="name">用户的名称</param>
|
||||||
/// <param name="email">[TODO:parameter]</param>
|
|
||||||
/// <param name="password">[TODO:parameter]</param>
|
|
||||||
/// <returns>插入的记录数</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
public int AddUser(string name, string email, string password)
|
public int AddUser(string name)
|
||||||
{
|
{
|
||||||
var user = new User()
|
var user = new User()
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name
|
||||||
EMail = email,
|
|
||||||
Password = password,
|
|
||||||
Permission = Database.User.UserPermission.Normal,
|
|
||||||
};
|
};
|
||||||
return this.Insert(user);
|
return this.Insert(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">[TODO:parameter]</param>
|
|
||||||
/// <param name="password">[TODO:parameter]</param>
|
|
||||||
/// <returns>[TODO:return]</returns>
|
|
||||||
public Result<Optional<User>> CheckUserPassword(string name, string password)
|
|
||||||
{
|
|
||||||
var user = this.User.Where((user) => user.Name == name).ToArray();
|
|
||||||
|
|
||||||
if (user.Length > 1)
|
|
||||||
{
|
|
||||||
logger.Error($"TODO");
|
|
||||||
return new(new Exception($""));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user[0].Password == password) return new(user[0]);
|
|
||||||
else return new(Optional.Null<User>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加一块新的 FPGA 板子到数据库
|
/// 添加一块新的 FPGA 板子到数据库
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">FPGA 板子的名称</param>
|
/// <param name="name">FPGA 板子的名称</param>
|
||||||
/// <param name="ipAddr">[TODO:Param]</param>
|
|
||||||
/// <param name="port">[TODO:Param]</param>
|
|
||||||
/// <returns>插入的记录数</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
public int AddBoard(string name, string ipAddr, int port)
|
public int AddBoard(string name)
|
||||||
{
|
{
|
||||||
var board = new Board()
|
var board = new Board()
|
||||||
{
|
{
|
||||||
BoardName = name,
|
BoardName = name
|
||||||
IpAddr = ipAddr,
|
|
||||||
Port = port,
|
|
||||||
Status = Database.Board.BoardStatus.Available,
|
|
||||||
};
|
};
|
||||||
return this.Insert(board);
|
return this.Insert(board);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>[TODO:return]</returns>
|
|
||||||
public Optional<Board> GetAvailableBoard()
|
|
||||||
{
|
|
||||||
var boards = this.Board.Where(
|
|
||||||
(board) => board.Status == Database.Board.BoardStatus.Available
|
|
||||||
).ToArray();
|
|
||||||
|
|
||||||
if (boards.Length < 0)
|
|
||||||
{
|
|
||||||
logger.Warn($"TODO");
|
|
||||||
return new(null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var board = boards[0];
|
|
||||||
board.Status = Database.Board.BoardStatus.Busy;
|
|
||||||
this.Board
|
|
||||||
.Where(target => target.ID == board.ID)
|
|
||||||
.Set(target => target.Status, board.Status)
|
|
||||||
.Update();
|
|
||||||
return new(board);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户表
|
/// 用户表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -113,16 +113,6 @@ public class I2c
|
||||||
|
|
||||||
// 写入数据到I2C FIFO写入口
|
// 写入数据到I2C FIFO写入口
|
||||||
{
|
{
|
||||||
var i2cData = new byte[data.Length * 4];
|
|
||||||
int i = 0;
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
i2cData[i++] = 0x00;
|
|
||||||
i2cData[i++] = 0x00;
|
|
||||||
i2cData[i++] = 0x00;
|
|
||||||
i2cData[i++] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.Write, data);
|
var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.Write, data);
|
||||||
if (!ret.IsSuccessful)
|
if (!ret.IsSuccessful)
|
||||||
{
|
{
|
||||||
|
|
|
@ -454,11 +454,13 @@ public class UDPClientPool
|
||||||
{
|
{
|
||||||
// Sperate Data Array
|
// Sperate Data Array
|
||||||
var isLastData = i == writeTimes - 1;
|
var isLastData = i == writeTimes - 1;
|
||||||
var sendDataArray = isLastData ?
|
var sendDataArray =
|
||||||
|
isLastData ?
|
||||||
dataArray[(i * (256 * (32 / 8)))..] :
|
dataArray[(i * (256 * (32 / 8)))..] :
|
||||||
dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)))];
|
dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)))];
|
||||||
|
|
||||||
opts.BurstLength = ((byte)Math.Min(sendDataArray.Length / 4 - 1, 1));
|
// Write Jtag State Register
|
||||||
|
opts.BurstLength = ((byte)(sendDataArray.Length / 4 - 1));
|
||||||
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
|
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
|
||||||
if (!ret) return new(new Exception("Send 1st address package failed!"));
|
if (!ret) return new(new Exception("Send 1st address package failed!"));
|
||||||
|
|
||||||
|
|
|
@ -269,9 +269,6 @@ namespace WebProtocol
|
||||||
if (bodyData.Length > 256 * (32 / 8))
|
if (bodyData.Length > 256 * (32 / 8))
|
||||||
throw new Exception("The data of SendDataPackage can't over 256 * 32bits");
|
throw new Exception("The data of SendDataPackage can't over 256 * 32bits");
|
||||||
|
|
||||||
if (bodyData.Length % 4 != 0)
|
|
||||||
throw new Exception("The data of SendDataPackage should be divided by 4");
|
|
||||||
|
|
||||||
this.bodyData = bodyData;
|
this.bodyData = bodyData;
|
||||||
|
|
||||||
_ = _reserved;
|
_ = _reserved;
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen bg-base-100 container mx-auto p-6 space-y-6">
|
<header></header>
|
||||||
<ul class="menu bg-base-200 w-56 gap-2 rounded-2xl">
|
<main class="relative">
|
||||||
<li><a>Item 1</a></li>
|
<div class="w-screen h-screen flex items-center justify-center">
|
||||||
<li><a>Item 2</a></li>
|
<UploadCard />
|
||||||
<li><a>Item 3</a></li>
|
</div>
|
||||||
</ul>
|
</main>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import UploadCard from "@/components/UploadCard.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
@import "../assets/main.css";
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue