diff --git a/BootstrapBlazorApp.Server/Components/Pages/HomePage.razor b/BootstrapBlazorApp.Server/Components/Pages/HomePage.razor index da6c61e8ff75e915ea2cd3ac36c91dbe7715bd61..718e57c3237be42b412cf9e01aa0a73c175d982f 100644 --- a/BootstrapBlazorApp.Server/Components/Pages/HomePage.razor +++ b/BootstrapBlazorApp.Server/Components/Pages/HomePage.razor @@ -1,4 +1,5 @@ -@page "/" +@page "/HomePage" +@using System.Security.Claims @using BootstrapBlazorApp.Server.Components.CustomComponent; @@ -69,8 +70,11 @@ @inject NavigationManager NavigationManager +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject SwalService SwalService @code { - private User user=new User(); + private ClaimsPrincipal? _user; + private User user; private List questions=new List(); private List status=new List(); private List questionsWithStatus=new List(); @@ -79,9 +83,24 @@ }; private int activeIndex = 0; - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { + base.OnInitializedAsync(); //获取当前用户对象 + _user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + + if (_user == null) + { + return; + } + + var value = _user.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (value != null) + { + var user_id = int.Parse(value); + user = User.Find(user_id); + } + //获取题目列表 GetAllQuestions(); user=User.Where(u => u.Id == 1).First(); diff --git a/BootstrapBlazorApp.Server/Components/Pages/Login.razor b/BootstrapBlazorApp.Server/Components/Pages/Login.razor new file mode 100644 index 0000000000000000000000000000000000000000..3cbdb70e4a80ab22a7127cc948c0955c0693fc0a --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Pages/Login.razor @@ -0,0 +1,78 @@ +@page "/" +@page "/Login" + +@using BootstrapBlazorApp.Server.Pojo +@inject AuthenticationStateProvider AuthenticationStateProvider +@layout LoginLayout +@inject AjaxService AjaxService +@inject SwalService SwalService +@inject NavigationManager NavigationManager + +@attribute [AllowAnonymous] + +@code +{ + private LoginVo LoginVo { get; set; } = new LoginVo(); + + private async Task OnValidSubmit(EditContext arg) + { + StateHasChanged(); + try + { + var document = await AjaxService.InvokeAsync(new AjaxOption() + { + Url = "/api/Account/Login", + Data = LoginVo + }); + + if (document?.RootElement.GetProperty("code").GetInt32() == 200) + { + await AjaxService.Goto("/HomePage"); //交换cookie + } + else + { + await SwalService.Show(new SwalOption() + { + Category = SwalCategory.Error, + Content = document?.RootElement.GetProperty("message").GetString() ?? "系统异常", + Title = "登录失败" + }); + } + } + catch (Exception ex) + { + // 处理网络请求或其他异常 + await SwalService.Show(new SwalOption() + { + Category = SwalCategory.Error, + Content = "发生错误:" + ex.Message, + Title = "系统错误" + }); + } + } + + + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + var user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + if (user?.Identity?.IsAuthenticated == true) + { + NavigationManager.NavigateTo("/HomePage"); + } + } +} \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Components/Pages/Login.razor.css b/BootstrapBlazorApp.Server/Components/Pages/Login.razor.css new file mode 100644 index 0000000000000000000000000000000000000000..61185820eec6f945cbc945f218bd10b158128f5b --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Pages/Login.razor.css @@ -0,0 +1,51 @@ +.login-item-gitee { + --bs-border-radius: 6px; + --login-padding-x: .75rem; + margin: 1.5rem auto; +} + +.login-item-gitee a { + color: #005980; +} + +.login-item-gitee .login-header { + display: flex; + justify-content: space-between; + align-items: flex-end; + margin-bottom: 1.5rem; +} + +.login-item-gitee .login-header > span { + font-size: 1.5rem; + font-weight: bold; +} + +.login-item-gitee .login-header > span { + font-size: 1.5rem; + font-weight: bold; +} + +.login-item-gitee .login-sms { + display: flex; + justify-content: space-between; + align-items: center; +} + +.login-item-gitee .login-sms > a { + padding: var(--login-padding-y) var(--login-padding-x); + margin-bottom: 1rem; +} + +.login-item-gitee .login-oauth { + display: flex; + justify-content: space-between; +} + +.login-item-gitee .login-oauth i { + font-size: 2rem; + color: #485585; +} + +.login-item-gitee ::deep .divider { + --bb-divider-margin: 2rem; +} diff --git a/BootstrapBlazorApp.Server/Components/Pages/PersonalPage.razor b/BootstrapBlazorApp.Server/Components/Pages/PersonalPage.razor index ec906f36a9a5f8b993b2de14b6882ef12dcd3a7d..08df012417331c964bfdbcc9b0967ea6fceff49e 100644 --- a/BootstrapBlazorApp.Server/Components/Pages/PersonalPage.razor +++ b/BootstrapBlazorApp.Server/Components/Pages/PersonalPage.razor @@ -1,5 +1,6 @@ -@page "/PersonalPage" +@page "/PersonalPage" @inject NavigationManager NavigationManager +@using System.Security.Claims @using Newtonsoft.Json; @using BootstrapBlazorApp.Server.Components.CustomComponent @@ -9,13 +10,6 @@ IsPage="true">
- @*logo*@ - @**@
@@ -82,7 +76,8 @@ @* 退出 *@
- + @**@ +
@@ -188,6 +183,7 @@ +@inject AuthenticationStateProvider AuthenticationStateProvider @code { private bool EditMode = false; @@ -201,38 +197,48 @@ private int mediumNum = 2; private int hardNum = 1; + private ClaimsPrincipal? _user; //用户信息 + private User user; - private User user=new User() - { - Id=1, - UserName = "bmmm", - Password = "123456", - NickName = "面包面面", - Avatar = "https://ww3.sinaimg.cn/mw690/d315af46ly1hnn5btbjr5j20j60j7mzv.jpg", - Introduction = "我是bmm,来自武汉大学" - }; - - private List users = []; - private User newUser = new(); - private List questions = []; - private List answerDetails= []; - private List questionsWithStatus = []; - protected override void OnInitialized() + private List users = new List(); + private User newUser = new User(); + private List questions = new List(); + private List answerDetails= new List(); + private List questionsWithStatus = new List(); + + protected override async Task OnInitializedAsync() { + base.OnInitialized(); + //获取当前用户对象 + _user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + + if (_user == null) + { + return; + } + + var value = _user.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (value != null) + { + var user_id = int.Parse(value); + user = User.Find(user_id); + } + GetAllHistory(); } private void GetAllHistory() { answerDetails=AnswerDetail.Where(a=> a.UserId == user.Id).ToList(); - /*questions= Question.Where(q=> q.Id == answerDetails[0].QuestionId).ToList();*/ - var questionIds = answerDetails.Select(ad => ad.QuestionId).Distinct(); - questions = Question - .Where(q => questionIds.Contains(q.Id)) - .Take(6) - .ToList(); + if(answerDetails!=null&&answerDetails.Count>0){ + var questionIds = answerDetails.Select(ad => ad.QuestionId).Distinct(); + questions = Question + .Where(q => questionIds.Contains(q.Id)) + .Take(6) + .ToList(); + } } private void Edit() diff --git a/BootstrapBlazorApp.Server/Components/Pages/QDetail.razor b/BootstrapBlazorApp.Server/Components/Pages/QDetail.razor index 2f48ba5d1425fe763769f1c5acce7271b5da6de4..44d13d4435fde05a3ca5a91f4fd3f48bce328074 100644 --- a/BootstrapBlazorApp.Server/Components/Pages/QDetail.razor +++ b/BootstrapBlazorApp.Server/Components/Pages/QDetail.razor @@ -1,4 +1,5 @@ @page "/qdetail/{Qid}" +@using System.Security.Claims @using BootstrapBlazorApp.Server.Components.CustomComponent @using COSXML.Model.Tag @@ -126,12 +127,14 @@ @inject NavigationManager NavigationManager +@inject AuthenticationStateProvider AuthenticationStateProvider @code { [Parameter] public string Qid { get; set; } public User user { get; set; } - + + private ClaimsPrincipal? _user; private Question question { get; set; } private bool Staus { get; set; } private string baseArea="30%"; @@ -146,14 +149,20 @@ { base.OnInitialized(); //获得用户信息 - user=new User() + _user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + + if (_user == null) + { + return; + } + + var value = _user.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (value != null) { - Id = 1, - UserName = "bmmm", - Password = "123456", - NickName = "面包面面", - Avatar = "https://ww3.sinaimg.cn/mw690/d315af46ly1hnn5btbjr5j20j60j7mzv.jpg" - }; + var user_id = int.Parse(value); + user = User.Find(user_id); + } + //获得题目信息 int qid = Convert.ToInt32(Qid); //查询数据库获得题目详细信息 diff --git a/BootstrapBlazorApp.Server/Components/Pages/Register.razor b/BootstrapBlazorApp.Server/Components/Pages/Register.razor new file mode 100644 index 0000000000000000000000000000000000000000..64125a543d2b5649cddc1f0cc89ca1c1e53d2e89 --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Pages/Register.razor @@ -0,0 +1,78 @@ +@page "/Register" +@using BootstrapBlazorApp.Server.Pojo +@layout LoginLayout +@inject AjaxService AjaxService +@inject SwalService SwalService +@inject NavigationManager NavigationManager + +@attribute [AllowAnonymous] + + +@code +{ + private RegisterVo RegisterVo { get; set; } = new RegisterVo(); + + private void ToLogin() + { + NavigationManager.NavigateTo("/Login"); + } + + private async Task OnValidSubmit(EditContext arg) + { + try + { + // 使用Ajax调用注册API + var document = await AjaxService.InvokeAsync(new AjaxOption() + { + Url = "/api/Account/Register", + Data = RegisterVo // 直接传递RegisterVo对象作为数据 + }); + + if (document?.RootElement.GetProperty("code").GetInt32() == 200) + { + await SwalService.Show(new SwalOption() + { + Category = SwalCategory.Success, + Content = "注册成功!", + Title = "欢迎" + }); + //此时无需保存交换cookie,故直接使用navigation跳转即可 + NavigationManager.NavigateTo("/Login"); + } + else + { + await SwalService.Show(new SwalOption() + { + Category = SwalCategory.Error, + Content = document?.RootElement.GetProperty("message").GetString() ?? "系统异常", + Title = "注册失败" + }); + } + } + catch (Exception ex) + { + // 处理网络请求或其他异常 + await SwalService.Show(new SwalOption() + { + Category = SwalCategory.Error, + Content = "发生错误:" + ex.Message, + Title = "系统错误" + }); + } + } +} \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Components/Pages/Register.razor.css b/BootstrapBlazorApp.Server/Components/Pages/Register.razor.css new file mode 100644 index 0000000000000000000000000000000000000000..a331fbcd5fba8e6ff088c36703bf6e20513a0442 --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Pages/Register.razor.css @@ -0,0 +1,60 @@ +.login-item-gitee { + --bs-border-radius: 6px; + --login-padding-x: .75rem; + margin: 1.5rem auto; + padding: 0 1.5rem; +} + +.login-item-gitee a { + color: #005980; +} + +.login-item-gitee .login-header { + display: flex; + justify-content: space-between; + align-items: flex-end; + margin-bottom: 1.5rem; +} + +.login-item-gitee .login-header > span { + font-size: 1.5rem; + font-weight: bold; +} + +.login-item-gitee .login-header > span { + font-size: 1.5rem; + font-weight: bold; +} + +.login-item-gitee .login-sms { + display: flex; + justify-content: space-between; + align-items: center; +} + +.login-item-gitee .login-sms > a { + padding: var(--login-padding-y) var(--login-padding-x); + margin-bottom: 1rem; +} + +.login-item-gitee .login-oauth { + display: flex; + justify-content: space-between; +} + +.login-item-gitee .login-oauth i { + font-size: 2rem; + color: #485585; +} + +.login-item-gitee ::deep .divider { + --bb-divider-margin: 2rem; +} +.login-item-floating { + --login-padding-x: 1rem; + --bs-border-radius: 10px; +} + +.text-dark{ + font-weight: bold; +} \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Components/Pages/Star.razor b/BootstrapBlazorApp.Server/Components/Pages/Star.razor index 0debf76ee81aab933c16d3027530617c4397ac70..047af718ce83221d5f17f4a09bcd70778a3a947b 100644 --- a/BootstrapBlazorApp.Server/Components/Pages/Star.razor +++ b/BootstrapBlazorApp.Server/Components/Pages/Star.razor @@ -1,4 +1,5 @@ @page "/Star" +@using System.Security.Claims @inject NavigationManager NavigationManager @using BootstrapBlazorApp.Server.Components.CustomComponent; @@ -61,22 +62,37 @@ +@inject AuthenticationStateProvider AuthenticationStateProvider @code { + private ClaimsPrincipal? _user; private string PlaceHolderText = "请输入用户名"; - private User user=new User() - { - Id=1, - UserName = "bmmm", - Password = "123456", - NickName = "面包面面", - Avatar = "https://ww3.sinaimg.cn/mw690/d315af46ly1hnn5btbjr5j20j60j7mzv.jpg", - Introduction = "我是bmm,来自武汉大学,嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻" - }; + private User user; + private User newUser=new User(); private List stars= new List(); private List questionWithStatus = new List(); public List questions = new List(); + protected override async Task OnInitializedAsync() + { + base.OnInitialized(); + //获取当前用户对象 + _user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + + if (_user == null) + { + return; + } + + var value = _user.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (value != null) + { + var user_id = int.Parse(value); + user = User.Find(user_id); + } + + } + private Task OnSearch(string searchText) { questionWithStatus.Clear(); diff --git a/BootstrapBlazorApp.Server/Components/Shared/LoginLayout.razor b/BootstrapBlazorApp.Server/Components/Shared/LoginLayout.razor new file mode 100644 index 0000000000000000000000000000000000000000..c45fb3ae269bd60b2ff40846aedd164565dd1727 --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Shared/LoginLayout.razor @@ -0,0 +1,24 @@ +@using System.Diagnostics.CodeAnalysis +@using BootstrapBlazorApp.Server.Pojo +@inherits LayoutComponentBase +@layout TutorialsLayout + +
+
+
+ + + + @Body + + + +
+
+
+
+@code +{ +[NotNull] +private LoginVo? Model { get; set; } = new(); +} \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Components/Shared/LoginLayout.razor.css b/BootstrapBlazorApp.Server/Components/Shared/LoginLayout.razor.css new file mode 100644 index 0000000000000000000000000000000000000000..99cbd45824bc3495a9462a2ce137e78b81a0abc2 --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Shared/LoginLayout.razor.css @@ -0,0 +1,34 @@ +.container { + --login-max-width: 540px; + --login-padding-x: 1.5rem; + --login-padding-y: .75rem; + --bs-border-radius: 23px; + max-width: var(--login-max-width); + margin: 0 auto; +} + +::deep h4 { + margin-bottom: 1.5rem; +} + +::deep .form-control { + --bb-form-control-padding: var(--login-padding-y) var(--login-padding-x); + font-size: .875rem; + margin-bottom: 1rem; +} + +::deep .btn { + --bs-btn-padding-x: var(--login-padding-x); + --bs-btn-padding-y: var(--login-padding-y); + width: 100%; + margin-bottom: 1rem; +} + +::deep .btn:first-of-type { + margin-bottom: 0; +} + +::deep .form-check { + padding: var(--login-padding-y) var(--login-padding-x); + margin-bottom: 1rem; +} diff --git a/BootstrapBlazorApp.Server/Components/Shared/MainLayout.razor b/BootstrapBlazorApp.Server/Components/Shared/MainLayout.razor index 4c0aaaeafc189806c382dc4c406e2db3060fb8ef..8a6e00ec3168975933a594522aaed14924ecfc69 100644 --- a/BootstrapBlazorApp.Server/Components/Shared/MainLayout.razor +++ b/BootstrapBlazorApp.Server/Components/Shared/MainLayout.razor @@ -1,6 +1,9 @@  @inherits LayoutComponentBase - - - @Body - + +
+ + @Body + +
+
diff --git a/BootstrapBlazorApp.Server/Components/Shared/TutorialsLayout.razor b/BootstrapBlazorApp.Server/Components/Shared/TutorialsLayout.razor new file mode 100644 index 0000000000000000000000000000000000000000..ff872258e1eb5da44a035874efd8a5a6478fb203 --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Shared/TutorialsLayout.razor @@ -0,0 +1,9 @@ +@inherits LayoutComponentBase + +
+ +
+ @Body +
+
+
\ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Components/Shared/TutorialsLayout.razor.css b/BootstrapBlazorApp.Server/Components/Shared/TutorialsLayout.razor.css new file mode 100644 index 0000000000000000000000000000000000000000..48e1524b33e7865780d92a464abcce4c605ae7f0 --- /dev/null +++ b/BootstrapBlazorApp.Server/Components/Shared/TutorialsLayout.razor.css @@ -0,0 +1,50 @@ +.section { + --bb-sidebar-width: 0; +} + +.main { + padding: 1rem; +} + +.sidebar-title { + height: 50px; + align-items: center; + padding: 1rem; + border-bottom: solid 1px var(--bs-border-color); + display: none; +} + +.sidebar-text { + font-weight: 700; +} + +::deep p:last-child { + margin-bottom: 0; +} + +@media (min-width: 768px) { + .section { + --bb-sidebar-width: 300px; + display: flex; + flex-direction: row; + -webkit-font-smoothing: antialiased; + } + + .sidebar-title { + display: flex; + } + + .sidebar { + width: var(--bb-sidebar-width); + height: calc(100vh); + position: sticky; + top: 0; + border-right: solid 1px var(--bs-border-color); + margin-top: calc(var(--bs-header-height) * -1); + } + + .main { + flex: 1; + height: 100%; + } +} diff --git a/BootstrapBlazorApp.Server/Components/_Imports.razor b/BootstrapBlazorApp.Server/Components/_Imports.razor index 40bed337921aabab6fa00c6b9ab83f68a9258f40..bb7b2d5869d7d4b359e677fc97a308012eb7d8ba 100644 --- a/BootstrapBlazorApp.Server/Components/_Imports.razor +++ b/BootstrapBlazorApp.Server/Components/_Imports.razor @@ -14,4 +14,6 @@ @using System.ComponentModel @using System.ComponentModel.DataAnnotations -@using System.Net.Http \ No newline at end of file +@using System.Net.Http + +@attribute [Authorize] \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Controllers/AccountController.cs b/BootstrapBlazorApp.Server/Controllers/AccountController.cs new file mode 100644 index 0000000000000000000000000000000000000000..205b910155a92af8e9a7987a30403297d0f25107 --- /dev/null +++ b/BootstrapBlazorApp.Server/Controllers/AccountController.cs @@ -0,0 +1,92 @@ +using System.Security.Claims; +using BootstrapBlazorApp.Server.Data; +using BootstrapBlazorApp.Server.Pojo; +using BootstrapBlazor.Components; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using BootstrapBlazorApp.Server.Utils; +using Microsoft.AspNetCore.Authorization; + +namespace BootstrapBlazorApp.Server.Controllers; + +[ApiController] +[Route("/api/[controller]/[action]")] +public class AccountController:ControllerBase +{ + //注入相关的数据库服务实例使用 + private readonly IDataService _userService; + + //自动查找注册到容器中的IDataService服务 + public AccountController(IDataService userService) + { + _userService = userService; + } + //增加API方法 + [HttpPost] + public async Task Login([FromBody] LoginVo loginVo) + { + var checkPassword=Md5Utils.Md5Encrypt32(loginVo.Password); + var user=await Data.User.Where(x => x.UserName == loginVo.UserName && x.Password==checkPassword).FirstAsync(); + //明文密码 + //var user=await Data.User.Where(x => x.UserName == loginVo.UserName && x.Password==loginVo.Password).FirstAsync(); + //查找不到对应用户 + if (user == null) + { + return new { Code = 500, Message = "用户名或密码错误" }; + } + + //创建包含用户认证的实例,用于cookie验证 + var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); + //将新的声明添加到 ClaimsIdentity 对象中 + identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName!)); + identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); + //用来登录用户 + await HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties() + { + IsPersistent = true, + ExpiresUtc = loginVo.IsKeep ? DateTimeOffset.Now.AddDays(1) : DateTimeOffset.Now.AddMinutes(30) + }); + return new { Code = 200, Message = "登陆成功" }; + } + + // 注册新用户的方法 + [HttpPost] + public async Task> Register([FromBody] RegisterVo registerVo) + { + var user=await Data.User.Where(x => x.UserName == registerVo.UserName).FirstAsync(); + //查找得到对应用户 + if (user != null) + { + return BadRequest(new { Code = 409, Message = "用户名已存在" }); + } + //加密 + var changedPassword = Md5Utils.Md5Encrypt32(registerVo.Password); + + // 创建新用户 + var newUser = new User + { + UserName = registerVo.UserName, + Password = changedPassword, + Avatar = "https://software-introduction.oss-cn-beijing.aliyuncs.com/1.jpg", + NickName = registerVo.UserName + + }; + var regResult = await _userService.SaveAsync(newUser, ItemChangedType.Add); // ItemChangedType提示 + if (!regResult) + { + return new { Code = 422, Message = "注册失败,请稍后再试" }; + } + + return new { Code = 200, Message = "注册成功" }; + } + + //登出方法 + [HttpGet] + [Authorize] + public async Task Logout() + { + await HttpContext.SignOutAsync(); //清除所有用户认证相关信息,包括cookie + return Redirect("/Login"); //重定向 + } +} \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Extensions/FreesqlExtensions.cs b/BootstrapBlazorApp.Server/Extensions/FreesqlExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..7a073c060015144bb60eedaf6b2b61cdd1a8a213 --- /dev/null +++ b/BootstrapBlazorApp.Server/Extensions/FreesqlExtensions.cs @@ -0,0 +1,94 @@ +using BootstrapBlazor.Components; +using FreeSql.Internal.Model; + +namespace BootstrapBlazorApp.Server.Extensions; + +public static class FreeSqlExtensions +{ + /// + /// + /// + /// + /// + public static DynamicFilterInfo ToDynamicFilter(this QueryPageOptions option) + { + var ret = new DynamicFilterInfo() { Filters = new() }; + + // 处理模糊搜索 + if (option.Searches.Any()) + { + ret.Filters.Add(new() + { + Logic = DynamicFilterLogic.Or, + Filters = option.Searches.Select(i => i.ToDynamicFilter()).ToList() + }); + } + + // 处理自定义搜索 + if (option.CustomerSearches.Any()) + { + ret.Filters.AddRange(option.CustomerSearches.Select(i => i.ToDynamicFilter())); + } + + // 处理高级搜索 + if (option.AdvanceSearches.Any()) + { + ret.Filters.AddRange(option.AdvanceSearches.Select(i => i.ToDynamicFilter())); + } + + // 处理表格过滤条件 + if (option.Filters.Any()) + { + ret.Filters.AddRange(option.Filters.Select(i => i.ToDynamicFilter())); + } + return ret; + } + + private static DynamicFilterInfo ToDynamicFilter(this IFilterAction filter) + { + var actions = filter.GetFilterConditions(); + var item = new DynamicFilterInfo(); + + if (actions.Filters != null) + { + // TableFilter 最多仅两个条件 + if (actions.Filters.Count == 2) + { + item.Logic = actions.FilterLogic.ToDynamicFilterLogic(); + item.Filters = actions.Filters.Select(i => new DynamicFilterInfo() + { + Field = i.FieldKey, + Value = i.FieldValue, + Operator = i.FilterAction.ToDynamicFilterOperator() + }).ToList(); + } + else + { + var c = actions.Filters.First(); + item.Field = c.FieldKey; + item.Value = c.FieldValue; + item.Operator = c.FilterAction.ToDynamicFilterOperator(); + } + } + return item; + } + + private static DynamicFilterLogic ToDynamicFilterLogic(this FilterLogic logic) => logic switch + { + FilterLogic.And => DynamicFilterLogic.And, + _ => DynamicFilterLogic.Or + }; + + private static DynamicFilterOperator ToDynamicFilterOperator(this FilterAction action) => action switch + { + FilterAction.Equal => DynamicFilterOperator.Equal, + FilterAction.NotEqual => DynamicFilterOperator.NotEqual, + FilterAction.Contains => DynamicFilterOperator.Contains, + FilterAction.NotContains => DynamicFilterOperator.NotContains, + FilterAction.GreaterThan => DynamicFilterOperator.GreaterThan, + FilterAction.GreaterThanOrEqual => DynamicFilterOperator.GreaterThanOrEqual, + FilterAction.LessThan => DynamicFilterOperator.LessThan, + FilterAction.LessThanOrEqual => DynamicFilterOperator.LessThanOrEqual, + _ => throw new System.NotSupportedException() + }; +} \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Pojo/LoginVO.cs b/BootstrapBlazorApp.Server/Pojo/LoginVO.cs new file mode 100644 index 0000000000000000000000000000000000000000..6a7792dd70da170316d8ff4f8741f08845e8e4c2 --- /dev/null +++ b/BootstrapBlazorApp.Server/Pojo/LoginVO.cs @@ -0,0 +1,13 @@ +namespace BootstrapBlazorApp.Server.Pojo; +using System.ComponentModel.DataAnnotations; + +public class LoginVo +{ + [Required(ErrorMessage = "用户名不可为空")] + public string? UserName { get; set; } + + [Required(ErrorMessage = "密码不可为空")] + public string? Password { get; set; } + + public bool IsKeep { get; set; } +} diff --git a/BootstrapBlazorApp.Server/Pojo/RegisterVO.cs b/BootstrapBlazorApp.Server/Pojo/RegisterVO.cs new file mode 100644 index 0000000000000000000000000000000000000000..8d1bffb0be803550b79b695540b591b7b2d89bf6 --- /dev/null +++ b/BootstrapBlazorApp.Server/Pojo/RegisterVO.cs @@ -0,0 +1,18 @@ +namespace BootstrapBlazorApp.Server.Pojo; + +using System.ComponentModel.DataAnnotations; + +public class RegisterVo +{ + [Required(ErrorMessage = "用户名不可为空")] + public string? UserName { get; set; } + + + [Required(ErrorMessage = "密码不可为空")] + public string? Password { get; set; } + + [Required(ErrorMessage = "确认密码不可为空")] + [Compare("Password", ErrorMessage = "密码与确认密码不匹配")] + public string? ConfirmPassword { get; set; } + +} diff --git a/BootstrapBlazorApp.Server/Program.cs b/BootstrapBlazorApp.Server/Program.cs index d745b628284fb68f05ec772ea6cb415a497f600b..5c0f19a5ab62c663bbb16beb12a5c903ec17093d 100644 --- a/BootstrapBlazorApp.Server/Program.cs +++ b/BootstrapBlazorApp.Server/Program.cs @@ -1,8 +1,12 @@ -using BootstrapBlazorApp.Server.Components; +using BootstrapBlazor.Components; +using BootstrapBlazorApp.Server.Components; using BootstrapBlazorApp.Server.Service; using FreeSql; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Caching.Memory; using System.Text; +using Console = System.Console; var builder = WebApplication.CreateBuilder(args); @@ -13,6 +17,23 @@ builder.Services.AddRazorComponents().AddInteractiveServerComponents(); builder.Services.AddBootstrapBlazor(); +//注入数据库服务 +builder.Services.AddScoped(typeof(IDataService<>), typeof(FreesqlDataService<>)); +//注入controller +builder.Services.AddControllers(); +//注入认证服务 +builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie( + config => + { + config.LoginPath = "/Login"; + }); +//注入cascding,级联身份验证状态 +builder.Services.AddCascadingAuthenticationState(); +//添加内存缓存服务 +builder.Services.AddSingleton(); + +builder.Services.Configure(option => option.MaximumReceiveMessageSize = null); + IFreeSql fsql = new FreeSqlBuilder() .UseConnectionString(DataType.PostgreSQL, @"Host=8.130.48.49;Port=5432;Username=dotnet;Password=adminsoftware; Database=dotnet;ArrayNullabilityMode=Always;Pooling=true;Minimum Pool Size=1") .UseMonitorCommand(cmd => Console.WriteLine($"Sql:{cmd.CommandText}")) @@ -24,17 +45,26 @@ BaseEntity.Initialization(fsql, null); builder.Services.Configure(option => option.MaximumReceiveMessageSize = null); // 增加文件上传服务 builder.Services.AddScoped(); + var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); + app.UseHsts(); } +app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAntiforgery(); +//处理身份验证 +app.UseAuthentication(); +//处理是否相应资源 +app.UseAuthorization(); +app.MapDefaultControllerRoute(); + app.MapRazorComponents().AddInteractiveServerRenderMode(); app.Run(); \ No newline at end of file diff --git a/BootstrapBlazorApp.Server/Service/FreesqlDataService.cs b/BootstrapBlazorApp.Server/Service/FreesqlDataService.cs new file mode 100644 index 0000000000000000000000000000000000000000..4a2b7f915cecb83c254784f614b10c71582c0659 --- /dev/null +++ b/BootstrapBlazorApp.Server/Service/FreesqlDataService.cs @@ -0,0 +1,63 @@ +using BootstrapBlazor.Components; +using BootstrapBlazorApp.Server.Extensions; +using FreeSql; + +namespace BootstrapBlazorApp.Server.Service; + +public class FreesqlDataService : DataServiceBase where TModel : class, new() +{ + private readonly IFreeSql _db = BaseEntity.Orm; + + /// + /// 删除方法 + /// + /// + /// + public override async Task DeleteAsync(IEnumerable models) + { + // 通过模型获取主键列数据 + // 支持批量删除 + await _db.Delete(models).ExecuteAffrowsAsync(); + return true; + } + + /// + /// 保存方法 + /// + /// + /// + /// + public override async Task SaveAsync(TModel model, ItemChangedType changedType) + { + await _db.GetRepository().InsertOrUpdateAsync(model); + return true; + } + + /// + /// 查询方法 + /// + /// + /// + public override Task> QueryAsync(QueryPageOptions option) + { + var select = _db.Select().WhereDynamicFilter(option.ToDynamicFilter()) + .OrderByPropertyNameIf(option.SortOrder != SortOrder.Unset, option.SortName, + option.SortOrder == SortOrder.Asc) + .Count(out var count); + if (option.IsPage) + { + select = select.Page(option.PageIndex, option.PageItems); + } + var Items = select.ToList(); + var ret = new QueryData() + { + TotalCount = (int)count, + Items = Items, + IsSorted = option.SortOrder != SortOrder.Unset, + IsFiltered = option.Filters.Any(), + IsAdvanceSearch = option.AdvanceSearches.Any(), + IsSearch = option.Searches.Any() || option.CustomerSearches.Any() + }; + return Task.FromResult(ret); + } +} diff --git "a/BootstrapBlazorApp.Server/wwwroot/logo_\347\231\275.png" "b/BootstrapBlazorApp.Server/wwwroot/logo_\347\231\275.png" new file mode 100644 index 0000000000000000000000000000000000000000..b16905d593e1963b609d4f804854b8fc83118309 Binary files /dev/null and "b/BootstrapBlazorApp.Server/wwwroot/logo_\347\231\275.png" differ diff --git "a/BootstrapBlazorApp.Server/wwwroot/logo_\351\273\221.png" "b/BootstrapBlazorApp.Server/wwwroot/logo_\351\273\221.png" new file mode 100644 index 0000000000000000000000000000000000000000..1a9fe383ad141ba806ec8808578a5554f8f6cd5d Binary files /dev/null and "b/BootstrapBlazorApp.Server/wwwroot/logo_\351\273\221.png" differ