diff --git a/Atomx.Admin/Atomx.Admin.Client/Atomx.Admin.Client.csproj b/Atomx.Admin/Atomx.Admin.Client/Atomx.Admin.Client.csproj new file mode 100644 index 0000000..1417e7b --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Atomx.Admin.Client.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + enable + enable + true + Default + + + + + + + + + + + + + + + + + + + + diff --git a/Atomx.Admin/Atomx.Admin.Client/Components/AuthorizePermissionView.razor b/Atomx.Admin/Atomx.Admin.Client/Components/AuthorizePermissionView.razor new file mode 100644 index 0000000..8a5b8ce --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Components/AuthorizePermissionView.razor @@ -0,0 +1,60 @@ +@using Microsoft.AspNetCore.Authorization +@inject IPermissionService PermissionService +@inject IAuthorizationService AuthorizationService + + + + + @if (_hasPermission) + { + @ChildContent + } + else if (!string.IsNullOrEmpty(NotAuthorizedContent)) + { + @NotAuthorizedContent + } + + + @if (!string.IsNullOrEmpty(NotAuthenticatedContent)) + { + @NotAuthenticatedContent + } + + + + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] public string? Permission { get; set; } + [Parameter] public string[]? Permissions { get; set; } + [Parameter] public bool RequireAll { get; set; } + [Parameter] public string? Policy { get; set; } + [Parameter] public string? NotAuthorizedContent { get; set; } + [Parameter] public string? NotAuthenticatedContent { get; set; } + + private bool _hasPermission; + + protected override async Task OnParametersSetAsync() + { + if (!string.IsNullOrEmpty(Policy)) + { + var authState = await AuthorizationService.AuthorizeAsync(null, Policy); + _hasPermission = authState.Succeeded; + } + else if (!string.IsNullOrEmpty(Permission)) + { + _hasPermission = await PermissionService.HasPermissionAsync(Permission); + } + else if (Permissions != null && Permissions.Length > 0) + { + if (RequireAll) + { + _hasPermission = await PermissionService.HasAllPermissionsAsync(Permissions); + } + else + { + _hasPermission = await PermissionService.HasAnyPermissionAsync(Permissions); + } + } + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Components/CultureSelector.razor b/Atomx.Admin/Atomx.Admin.Client/Components/CultureSelector.razor new file mode 100644 index 0000000..da084a0 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Components/CultureSelector.razor @@ -0,0 +1,29 @@ +@inject IJSRuntime JSRuntime +@inject ILocalizationService LocalizationService +@inject NavigationManager Navigation + +@* + English + 中文 + 日本語 + *@ + +@code { + private string _selectedCulture = "en-US"; + + protected override async Task OnInitializedAsync() + { + _selectedCulture = await JSRuntime.InvokeAsync("blazorCulture.get") ?? "en-US"; + } + + private async Task OnCultureChanged(ChangeEventArgs e) + { + var culture = e.Value?.ToString(); + if (!string.IsNullOrEmpty(culture)) + { + await JSRuntime.InvokeVoidAsync("blazorCulture.set", culture); + await LocalizationService.LoadResourcesAsync(culture); + Navigation.NavigateTo(Navigation.Uri, forceLoad: true); + } + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Components/LocalizedText.razor b/Atomx.Admin/Atomx.Admin.Client/Components/LocalizedText.razor new file mode 100644 index 0000000..7aaea5a --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Components/LocalizedText.razor @@ -0,0 +1,26 @@ +@inject ILocalizationService LocalizationService + +@Text + +@code { + private string? _text; + + [Parameter] + public string Key { get; set; } = string.Empty; + + [Parameter] + public string? Culture { get; set; } + + private string Text => _text ?? Key; + + protected override async Task OnParametersSetAsync() + { + await LoadText(); + } + + private async Task LoadText() + { + _text = await LocalizationService.GetStringAsync(Key, Culture) ?? Key; + StateHasChanged(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Layout/EmptyLayout.razor b/Atomx.Admin/Atomx.Admin.Client/Layout/EmptyLayout.razor new file mode 100644 index 0000000..e1953d3 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Layout/EmptyLayout.razor @@ -0,0 +1,9 @@ +@inherits LayoutComponentBase + + + @Body + + +@code { + +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Layout/MainLayout.razor b/Atomx.Admin/Atomx.Admin.Client/Layout/MainLayout.razor new file mode 100644 index 0000000..e40de20 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Layout/MainLayout.razor @@ -0,0 +1,122 @@ +@inherits LayoutComponentBase +@inject ILogger _logger + + + + + + + + + + + + + + @* + + *@ + + + + + + + + + + + + + @* + + *@ + + + + @Body + + + + + + + 程序遇到了未知错误! + + 确定 + + + @($"{ex.Message}{Environment.NewLine}{ex.StackTrace}") + + + + + + + + +@code { + private ErrorBoundary? _errorBoundary; + + private void ResetError(Exception ex) + { + _logger.LogError(ex, "未处理的异常。"); + _errorBoundary?.Recover(); + } + + private bool _open = true; + + private void ToggleDrawer() + { + _open = !_open; + } + + private MenuDataItem[] MenuDatas; + + private AvatarMenuItem[] AvatarMenuItems => + [ + new() { Key = "center", IconType = "user", Option = "通知消息"}, + new() { Key = "setting", IconType = "setting", Option ="修改资料" }, + new() { IsDivider = true }, + new() { Key = "logout", IconType = "logout", Option = "退出登录"} + ]; + + public void HandleSelectUser(AntDesign.MenuItem item) + { + switch (item.Key) + { + case "center": + Navigation.NavigateTo("/account/center"); + break; + case "setting": + Navigation.NavigateTo("/account/settings"); + break; + case "logout": + Navigation.NavigateTo("/logout"); + break; + } + } + + protected async override Task OnInitializedAsync() + { + + var url = "/api/menu/tree"; + var apiResult = await HttpService.Get>>(url); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + MenuDatas = apiResult.Data.ToArray(); + StateHasChanged(); + } + } + } + + +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Layout/MainLayout.razor.css b/Atomx.Admin/Atomx.Admin.Client/Layout/MainLayout.razor.css new file mode 100644 index 0000000..38d1f25 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Layout/MainLayout.razor.css @@ -0,0 +1,98 @@ +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; + } + + .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; + } + + .top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row { + justify-content: space-between; + } + + .top-row ::deep a, .top-row ::deep .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row.auth ::deep a:first-child { + flex: 1; + text-align: right; + width: 0; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} + +#blazor-error-ui { + color-scheme: light only; + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + box-sizing: border-box; + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/Atomx.Admin/Atomx.Admin.Client/Layout/NavMenu.razor b/Atomx.Admin/Atomx.Admin.Client/Layout/NavMenu.razor new file mode 100644 index 0000000..a835ded --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Layout/NavMenu.razor @@ -0,0 +1,30 @@ + + + Atomx.Admin + + + + + + + + + + Home + + + + + + Counter + + + + + + Weather + + + + + diff --git a/Atomx.Admin/Atomx.Admin.Client/Layout/NavMenu.razor.css b/Atomx.Admin/Atomx.Admin.Client/Layout/NavMenu.razor.css new file mode 100644 index 0000000..a2aeace --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Layout/NavMenu.razor.css @@ -0,0 +1,105 @@ +.navbar-toggler { + appearance: none; + cursor: pointer; + width: 3.5rem; + height: 2.5rem; + color: white; + position: absolute; + top: 0.5rem; + right: 1rem; + border: 1px solid rgba(255, 255, 255, 0.1); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); +} + +.navbar-toggler:checked { + background-color: rgba(255, 255, 255, 0.5); +} + +.top-row { + min-height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.bi { + display: inline-block; + position: relative; + width: 1.25rem; + height: 1.25rem; + margin-right: 0.75rem; + top: -1px; + background-size: cover; +} + +.bi-house-door-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); +} + +.bi-plus-square-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); +} + +.bi-list-nested-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep .nav-link { + color: #d7d7d7; + background: none; + border: none; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + width: 100%; + } + +.nav-item ::deep a.active { + background-color: rgba(255,255,255,0.37); + color: white; +} + +.nav-item ::deep .nav-link:hover { + background-color: rgba(255,255,255,0.1); + color: white; +} + +.nav-scrollable { + display: none; +} + +.navbar-toggler:checked ~ .nav-scrollable { + display: block; +} + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .nav-scrollable { + /* Never collapse the sidebar for wide screens */ + display: block; + + /* Allow sidebar to scroll for tall menus */ + height: calc(100vh - 3.5rem); + overflow-y: auto; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/AddressModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/AddressModel.cs new file mode 100644 index 0000000..f7db495 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/AddressModel.cs @@ -0,0 +1,85 @@ +namespace Atomx.Admin.Client.Models +{ + public class AddressModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 添加地址用户 + /// + public long UserId { get; set; } + + /// + /// 收件人姓名 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 邮件地址 + /// + public string Email { get; set; } = string.Empty; + + /// + /// 电话号码 + /// + public string Phone { get; set; } = string.Empty; + + /// + /// 公司 + /// + public string Company { get; set; } = string.Empty; + + /// + /// 国家 + /// + public long Country { get; set; } + + /// + /// 省份 + /// + public long Province { get; set; } + + /// + /// 城市 + /// + public long City { get; set; } + + /// + /// 地区 + /// + public long Region { get; set; } + + /// + /// 邮政编码 + /// + public string PostalCode { get; set; } = string.Empty; + + /// + /// 详细地址 + /// + public string FullAddress { get; set; } = string.Empty; + + /// + /// 地址所在经度 + /// + public decimal Longitude { get; set; } + + /// + /// 地址所在纬度 + /// + public decimal Latitude { get; set; } + + /// + /// 是否是虚拟地址 + /// + public bool IsVirtual { get; set; } + + /// + /// 是否编辑 + /// + public bool IsEdit { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/AdminModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/AdminModel.cs new file mode 100644 index 0000000..b5f22a1 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/AdminModel.cs @@ -0,0 +1,50 @@ +namespace Atomx.Admin.Client.Models +{ + public class AdminModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 管理员用户名 + /// + public string Username { get; set; } = string.Empty; + + /// + /// 注册手机号 + /// + public string Mobile { get; set; } = string.Empty; + + /// + /// 注册邮箱 + /// + public string Email { get; set; } = string.Empty; + + /// + /// 登录密码 + /// + public string Password { get; set; } = string.Empty; + + /// + /// 新密码 + /// + public string NewPassword { get; set; } = string.Empty; + + /// + /// 确认密码 + /// + public string RePassword { get; set; } = string.Empty; + + /// + /// 是否设置密码 + /// + public bool SetPassword { get; set; } + + /// + /// 状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/AdminSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/AdminSearch.cs new file mode 100644 index 0000000..2789a93 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/AdminSearch.cs @@ -0,0 +1,35 @@ +namespace Atomx.Admin.Client.Models +{ + public class AdminSearch : BaseSearch + { + /// + /// 用户名或昵称 + /// + public string? Username { get; set; } + + /// + /// 邮件地址 + /// + public string? Email { get; set; } + + /// + /// 手机号码 + /// + public string? Mobile { get; set; } + + /// + /// 真实姓名 + /// + public string? RealName { get; set; } + + /// + /// 开始时间 + /// + public DateTime?[] RangeTime { get; set; } = new DateTime?[] { null, null }; + + /// + /// 状态 + /// + public string? Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/AppVersionModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/AppVersionModel.cs new file mode 100644 index 0000000..2f04c01 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/AppVersionModel.cs @@ -0,0 +1,78 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace Atomx.Admin.Client.Models +{ + public class AppVersionModel + { + /// + /// 数据ID + /// + [DatabaseGenerated(DatabaseGeneratedOption.None)] + [Key] + public long Id { get; set; } + + /// + /// 运行平台 + /// + public int Platform { get; set; } + + /// + /// 应用名称KEY + /// + [Column(TypeName = "varchar(64)")] + public string AppName { get; set; } = string.Empty; + + /// + /// 版本标题 + /// + [Column(TypeName = "varchar(64)")] + public string Title { get; set; } = string.Empty; + + /// + /// 版本 + /// + [Column(TypeName = "varchar(64)")] + public string Version { get; set; } = string.Empty; + + /// + /// 主版本号(major)无法向下兼容时,需要递增 + /// + [Column(TypeName = "varchar(64)")] + public int VersionX { get; set; } + + /// + /// 次版本号(minor)新增新的特性时,需要递增 + /// + [Column(TypeName = "varchar(64)")] + public int VersionY { get; set; } + + /// + /// 修订版本号(patch)修复问题时,需要递增 + /// + [Column(TypeName = "varchar(64)")] + public int VersionZ { get; set; } + + /// + /// 版本日期 + /// + [Column(TypeName = "varchar(64)")] + public int VersionDate { get; set; } + + /// + /// 版本状态 例如:b 表示bate版,即测试版 + /// + public int VersionState { get; set; } + + /// + /// 更新内容说明 + /// + [Column(TypeName = "text")] + public string Content { get; set; } = string.Empty; + + /// + /// 版本状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/AppVersionSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/AppVersionSearch.cs new file mode 100644 index 0000000..9744577 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/AppVersionSearch.cs @@ -0,0 +1,20 @@ +namespace Atomx.Admin.Client.Models +{ + public class AppVersionSearch : BaseSearch + { + /// + /// 名称 + /// + public string? Name { get; set; } + + /// + /// 状态 + /// + public string Status { get; set; } = string.Empty; + + /// + /// 开始时间 + /// + public DateTime?[] RangeTime { get; set; } = new DateTime?[] { null, null }; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/BaseSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/BaseSearch.cs new file mode 100644 index 0000000..d040321 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/BaseSearch.cs @@ -0,0 +1,15 @@ +namespace Atomx.Admin.Client.Models +{ + public class BaseSearch + { + /// + /// 查询开始时间 + /// + public DateTime? StartTime { get; set; } + + /// + /// 查询结束时间 + /// + public DateTime? EndTime { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CategoryItem.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CategoryItem.cs new file mode 100644 index 0000000..352f346 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CategoryItem.cs @@ -0,0 +1,11 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class CategoryItem : Category + { + public List Children { get; set; } = new List(); + + public bool IsLast { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CategoryModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CategoryModel.cs new file mode 100644 index 0000000..b497973 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CategoryModel.cs @@ -0,0 +1,83 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace Atomx.Admin.Client.Models +{ + public class CategoryModel + { + /// + /// 分类ID + /// + [DatabaseGenerated(DatabaseGeneratedOption.None)] + [Key] + public long Id { get; set; } + + /// + /// 分类类型,1内容,2产品,3社区 + /// + public int Type { get; set; } + + /// + /// 上级分类ID + /// + public long ParentId { get; set; } + + /// + /// 分类名称 + /// + [Column(TypeName = "varchar(25)")] + public string Name { get; set; } = string.Empty; + + /// + /// 分类URL缩略名 + /// + [Column(TypeName = "varchar(50)")] + public string Slug { get; set; } = string.Empty; + + /// + /// Meta描述介绍 + /// + [Column(TypeName = "varchar(255)")] + public string MetaDescription { get; set; } = string.Empty; + + /// + /// Meta关键词 + /// + [Column(TypeName = "varchar(255)")] + public string MetaKeywords { get; set; } = string.Empty; + + /// + /// 过滤属性IDs + /// + [Column(TypeName = "varchar(255)")] + public string FilterAttributes { get; set; } = string.Empty; + + /// + /// 分类图片 + /// + [Column(TypeName = "varchar(255)")] + public string Image { get; set; } = string.Empty; + + /// + /// 分类页 banner + /// + [Column(TypeName = "varchar(255)")] + public string Banner { get; set; } = string.Empty; + + /// + /// 只是一个节点 + /// + public bool IsNode { get; set; } + + /// + /// 是否可用 + /// + + public bool Enabled { get; set; } + + /// + /// 排序 + /// + public int DisplayOrder { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CategorySearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CategorySearch.cs new file mode 100644 index 0000000..b4a93c2 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CategorySearch.cs @@ -0,0 +1,10 @@ +namespace Atomx.Admin.Client.Models +{ + public class CategorySearch : BaseSearch + { + /// + /// 分类名称 + /// + public string? Name { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CorporationModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationModel.cs new file mode 100644 index 0000000..9b68bdd --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationModel.cs @@ -0,0 +1,20 @@ +namespace Atomx.Admin.Client.Models +{ + public class CorporationModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 公司名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 数据状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CorporationSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationSearch.cs new file mode 100644 index 0000000..18dd7ad --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationSearch.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class CorporationSearch : BaseSearch + { + /// + /// 名称 + /// + public string? Name { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CorporationStaffModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationStaffModel.cs new file mode 100644 index 0000000..8806c1b --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationStaffModel.cs @@ -0,0 +1,55 @@ +namespace Atomx.Admin.Client.Models +{ + public class CorporationStaffModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 公司ID + /// + public long CorporationId { get; set; } + + /// + /// 管理员用户名 + /// + public string Username { get; set; } = string.Empty; + + /// + /// 注册手机号 + /// + public string Mobile { get; set; } = string.Empty; + + /// + /// 注册邮箱 + /// + public string Email { get; set; } = string.Empty; + + /// + /// 登录密码 + /// + public string Password { get; set; } = string.Empty; + + /// + /// 新密码 + /// + public string NewPassword { get; set; } = string.Empty; + + /// + /// 确认密码 + /// + public string RePassword { get; set; } = string.Empty; + + /// + /// 是否设置密码 + /// + public bool SetPassword { get; set; } + + /// + /// 状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CorporationStaffSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationStaffSearch.cs new file mode 100644 index 0000000..c158704 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationStaffSearch.cs @@ -0,0 +1,40 @@ +namespace Atomx.Admin.Client.Models +{ + public class CorporationStaffSearch : BaseSearch + { + /// + /// 公司ID + /// + public long CorporationId { get; set; } + + /// + /// 用户名或昵称 + /// + public string? Username { get; set; } + + /// + /// 邮件地址 + /// + public string? Email { get; set; } + + /// + /// 手机号码 + /// + public string? Mobile { get; set; } + + /// + /// 真实姓名 + /// + public string? RealName { get; set; } + + /// + /// 开始时间 + /// + public DateTime?[] RangeTime { get; set; } = new DateTime?[] { null, null }; + + /// + /// 状态 + /// + public string? Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CorporationUserModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationUserModel.cs new file mode 100644 index 0000000..b8d0f41 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationUserModel.cs @@ -0,0 +1,27 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class CorporationUserModel:User + { + /// + /// 公司ID + /// + public long CorporationId { get; set; } + + /// + /// 用户ID + /// + public long UserId { get; set; } + + /// + /// 是否是管理员 + /// + public bool IsAdmin { get; set; } + + /// + /// 数据状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CorporationUserSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationUserSearch.cs new file mode 100644 index 0000000..e01efc8 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CorporationUserSearch.cs @@ -0,0 +1,7 @@ +namespace Atomx.Admin.Client.Models +{ + public class CorporationUserSearch : BaseSearch + { + public string Name { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CurrencyModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CurrencyModel.cs new file mode 100644 index 0000000..b6af7a4 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CurrencyModel.cs @@ -0,0 +1,56 @@ +namespace Atomx.Admin.Client.Models +{ + public class CurrencyModel + { + /// + /// 数据ID + /// + public int Id { get; set; } + + /// + /// 语言 + /// + public string Language { get; set; } = string.Empty; + + /// + /// 货币名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 货币单位代码 + /// + public string CurrencyCode { get; set; } = string.Empty; + + /// + /// 本地显示 + /// + public string DisplayLocale { get; set; } = string.Empty; + + /// + /// 数字自定义格式化 + /// + public string CustomFormatting { get; set; } = string.Empty; + + /// + /// 兑换汇率 + /// + public double Rate { get; set; } + + /// + /// 排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 是否可用 + /// + + public bool Enabled { get; set; } + + /// + /// 是否编辑 + /// + public bool IsEdit { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/CurrentUser.cs b/Atomx.Admin/Atomx.Admin.Client/Models/CurrentUser.cs new file mode 100644 index 0000000..72d8e4c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/CurrentUser.cs @@ -0,0 +1,9 @@ +namespace Atomx.Admin.Client.Models +{ + public class CurrentUser + { + public string Name { get; set; } = string.Empty; + public string Avatar { get; set; } = string.Empty; + public string UserId { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/LanguageModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/LanguageModel.cs new file mode 100644 index 0000000..036bf28 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/LanguageModel.cs @@ -0,0 +1,55 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Atomx.Admin.Client.Models +{ + public class LanguageModel + { + /// + /// 数据ID + /// + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Key] + public int Id { get; set; } + + /// + /// 语言名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 语言标题 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 语言名称 + /// + public string Culture { get; set; } = string.Empty; + + /// + /// 国旗 + /// + public string FlagImage { get; set; } = string.Empty; + + /// + /// 排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 是否可用,系统面 + /// + public bool Enabled { get; set; } + + /// + /// 多语言资源的版本,可以是时间戳或哈希 + /// + public string ResourceVersion { get; set; } = string.Empty; + + /// + /// 是否编辑 + /// + public bool IsEdit { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/LanguageSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/LanguageSearch.cs new file mode 100644 index 0000000..46b7608 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/LanguageSearch.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Atomx.Admin.Client.Models +{ + public class LanguageSearch : BaseSearch + { + /// + /// 语言名称 + /// + public string Name { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/LinkItem.cs b/Atomx.Admin/Atomx.Admin.Client/Models/LinkItem.cs new file mode 100644 index 0000000..43a1ca7 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/LinkItem.cs @@ -0,0 +1,35 @@ +namespace Atomx.Common.Models +{ + public class LinkItem + { + /// + /// 链接标识 + /// + public string Key { get; set; } = string.Empty; + + /// + /// 链接名字 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 标题 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 图标 + /// + public string Image { get; set; } = string.Empty; + + /// + /// 链接URL + /// + public string Url { get; set; } = string.Empty; + + /// + /// 是否可用 + /// + public bool Enabled { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/LocaleResourceModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/LocaleResourceModel.cs new file mode 100644 index 0000000..a2843a4 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/LocaleResourceModel.cs @@ -0,0 +1,35 @@ +namespace Atomx.Admin.Client.Models +{ + public class LocaleResourceModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 语言编码 + /// + public int LanguageId { get; set; } + + /// + /// 语言名称 + /// + public string Culture { get; set; } = string.Empty; + + /// + /// 资源名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 资源内容值 + /// + public string Value { get; set; } = string.Empty; + + /// + /// 是否编辑 + /// + public bool IsEdit { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/LoginModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/LoginModel.cs new file mode 100644 index 0000000..1f51a6d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/LoginModel.cs @@ -0,0 +1,25 @@ +namespace Atomx.Admin.Client.Models +{ + public class LoginModel + { + /// + /// 登录账号 + /// + public string Account { get; set; } = string.Empty; + + /// + /// 登录密码 + /// + public string Password { get; set; } = string.Empty; + + /// + /// 地区代码,同时用来识别是否手机登录 + /// + public string AreaCode { get; set; } = string.Empty; + + /// + /// 是否记住我 + /// + public bool SaveMe { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerModel.cs new file mode 100644 index 0000000..59a9bdb --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerModel.cs @@ -0,0 +1,58 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace Atomx.Admin.Client.Models +{ + public class ManufacturerModel + { + /// + /// 制造商ID + /// + public long Id { get; set; } + + /// + /// 制造商名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 是否是供应商 + /// + public bool IsVendor { get; set; } + + /// + /// 品牌LOGO + /// + public string Logo { get; set; } = string.Empty; + + /// + /// 品牌制造商描述 + /// + public string Description { get; set; } = string.Empty; + + /// + /// Meta标题 + /// + public string MetaTitle { get; set; } = string.Empty; + + /// + /// Meta标题 + /// + public string MetaKeywords { get; set; } = string.Empty; + + /// + /// Meta标题 + /// + public string MetaDescription { get; set; } = string.Empty; + + /// + /// 是否启用 + /// + public bool Enabled { get; set; } + + /// + /// 显示排序 + /// + public int DisplayOrder { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerProductItemSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerProductItemSearch.cs new file mode 100644 index 0000000..026227c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerProductItemSearch.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class ManufacturerProductItemSearch : BaseSearch + { + + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerSearch.cs new file mode 100644 index 0000000..fd85166 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ManufacturerSearch.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class ManufacturerSearch:BaseSearch + { + /// + /// 名称 + /// + public string Name { get; set; }=string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MaterialSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MaterialSearch.cs new file mode 100644 index 0000000..5b5630c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MaterialSearch.cs @@ -0,0 +1,20 @@ +namespace Atomx.Admin.Client.Models +{ + public class MaterialSearch : BaseSearch + { + /// + /// 类型 + /// + public int? Type { get; set; } + + /// + /// 原料归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 原料归属店铺网点ID + /// + public long StoreId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MenuItem.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MenuItem.cs new file mode 100644 index 0000000..67e159c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MenuItem.cs @@ -0,0 +1,9 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class MenuItem : Menu + { + public List? Children { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MenuModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MenuModel.cs new file mode 100644 index 0000000..7eab037 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MenuModel.cs @@ -0,0 +1,70 @@ +namespace Atomx.Admin.Client.Models +{ + public class MenuModel + { + /// + /// 分类ID + /// + public long Id { get; set; } + + /// + /// 分类类型,1-管理后台系统 + /// + public int Type { get; set; } + + /// + /// 上级分类ID + /// + public long ParentId { get; set; } + + /// + /// 分类名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 分类图片 + /// + public string Icon { get; set; } = string.Empty; + + /// + /// 菜单分组,针对折叠菜单效果 + /// + public string Key { get; set; } = string.Empty; + + /// + /// 菜单链接的URL + /// + public string Url { get; set; } = string.Empty; + + /// + /// 权限代码 + /// + public string Code { get; set; } = string.Empty; + + /// + /// 是否外链接 + /// + public bool IsLink { get; set; } + + /// + /// 是否可用 + /// + public bool Enabled { get; set; } + + /// + /// 层级深度 + /// + public int Depth { get; set; } + + /// + /// 层级路径 + /// + public string Path { get; set; } = string.Empty; + + /// + /// 排序 + /// + public int DisplayOrder { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MenuSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MenuSearch.cs new file mode 100644 index 0000000..4452779 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MenuSearch.cs @@ -0,0 +1,10 @@ +namespace Atomx.Admin.Client.Models +{ + public class MenuSearch + { + /// + /// 名称 + /// + public string Name { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MenuTreeItem.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MenuTreeItem.cs new file mode 100644 index 0000000..175f1fe --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MenuTreeItem.cs @@ -0,0 +1,19 @@ +namespace Atomx.Admin.Client.Models +{ + public class MenuTreeItem + { + public string Icon { get; set; } = string.Empty; + + public string IconFont { get; set; } = string.Empty; + + public string Locale { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; + + public string Key { get; set; } = string.Empty; + + public string Path { get; set; } = string.Empty; + + public List Children { get; set; } = new List(); + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MessageTemplateModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MessageTemplateModel.cs new file mode 100644 index 0000000..94095d5 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MessageTemplateModel.cs @@ -0,0 +1,40 @@ +namespace Atomx.Admin.Client.Models +{ + public class MessageTemplateModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 消息模板类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 信息模板KEY + /// + public string Key { get; set; } = string.Empty; + + /// + /// 消息模板名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 消息标题 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 消息内容 + /// + public string Body { get; set; } = string.Empty; + + /// + /// 是否可用 + /// + public bool Enabled { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/MessageTemplateSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/MessageTemplateSearch.cs new file mode 100644 index 0000000..a906ad2 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/MessageTemplateSearch.cs @@ -0,0 +1,9 @@ +namespace Atomx.Admin.Client.Models +{ + public class MessageTemplateSearch : BaseSearch + { + public int? Type { get; set; } + + public string Key { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/OrderSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/OrderSearch.cs new file mode 100644 index 0000000..6bc611c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/OrderSearch.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class OrderSearch : BaseSearch + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/PriceTrendSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/PriceTrendSearch.cs new file mode 100644 index 0000000..93c37d3 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/PriceTrendSearch.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class PriceTrendSearch : BaseSearch + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAddStockModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAddStockModel.cs new file mode 100644 index 0000000..12fbd13 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAddStockModel.cs @@ -0,0 +1,50 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductAddStockModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 产品ID + /// + public long ProductId { get; set; } + + /// + /// 产品标题 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 产品属性 + /// + public List ProductStockAttributes { get; set; } = new(); + + /// + /// SKU信息 + /// + public ProductAttributeCombinationModel ProductAttributeCombination { get; set; } = new(); + + /// + /// 归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 仓库ID + /// + public string WarehouseId { get; set; } = string.Empty; + + /// + /// 是否新增属性 + /// + public bool AddAttribute { get; set; } + + /// + /// 添加库存 + /// + public int AddStockQuantity { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeCombinationModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeCombinationModel.cs new file mode 100644 index 0000000..f518b2d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeCombinationModel.cs @@ -0,0 +1,89 @@ +namespace Atomx.Admin.Client.Models +{ + /// + /// 产品SKU属性组合模型 + /// + public class ProductAttributeCombinationModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 产品ID + /// + public long ProductId { get; set; } + + /// + /// 供应制造商ID + /// + public long ManufacturerId { get; set; } + + /// + /// 属性JSON + /// + public List SkuAttributes { get; set; } = new List(); + + /// + /// 库存数量 + /// + public int StockQuantity { get; set; } + + /// + /// 销售数量 + /// + public int SalesQuantity { get; set; } + + /// + /// SKU编码 + /// + public string SkuNumber { get; set; } = string.Empty; + + /// + /// 销售加工费 + /// + public decimal ProcessCharge { get; set; } + + /// + /// 加工费成本 + /// + public decimal ProcessCost { get; set; } + + /// + /// 销售附加费 + /// + public decimal Surcharge { get; set; } + + /// + /// 附加费成本 + /// + public decimal SurchargeCost { get; set; } + + /// + /// SKU的市场价,划线价 + /// + public decimal MarketPrice { get; set; } + + /// + /// SKU的销售价格 + /// + + public decimal Price { get; set; } + + /// + /// 重量 + /// + public decimal Weight { get; set; } + + /// + /// 重量单位,1克,2千克,3磅,4盎司 + /// + public int WeightUnit { get; set; } + + /// + /// 是否上架销售 + /// + public bool Enabled { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeItemModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeItemModel.cs new file mode 100644 index 0000000..08dce14 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeItemModel.cs @@ -0,0 +1,10 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class ProductAttributeItemModel : ProductAttribute + { + + public string CategoryName { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeModel.cs new file mode 100644 index 0000000..3ac3f25 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeModel.cs @@ -0,0 +1,50 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductAttributeModel + { + /// + /// 属性ID + /// + public long Id { get; set; } + + /// + /// 上级属性,用于规格属性做分组 + /// + public long ParentId { get; set; } + + /// + /// 分类ID + /// + public long CategoryId { get; set; } + + /// + /// 属性名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 属性控件类型,1选项,0文本 + /// + public int ControlType { get; set; } + + /// + /// 必须填写重量 + /// + public bool WeightIsRequired { get; set; } + + /// + /// 是否是必须的属性 + /// + public bool IsRequired { get; set; } + + /// + /// 展示排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 数据状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeOptionModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeOptionModel.cs new file mode 100644 index 0000000..780946c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeOptionModel.cs @@ -0,0 +1,40 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductAttributeOptionModel + { + /// + /// 预设值ID + /// + public long Id { get; set; } + + /// + /// 供应商自定义值 + /// + public long VendorId { get; set; } + + /// + /// 产品属性ID + /// + public long AttributeId { get; set; } + + /// + /// 预设值名称 + /// + public string Value { get; set; } = string.Empty; + + /// + /// 展示排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 是否通用的 + /// + public bool Standard { get; set; } + + /// + /// 数据状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeOptionSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeOptionSearch.cs new file mode 100644 index 0000000..82065af --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeOptionSearch.cs @@ -0,0 +1,12 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductAttributeOptionSearch : BaseSearch + { + /// + /// 值 + /// + public string? Value { get; set; } + + public long AttributeId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeSearch.cs new file mode 100644 index 0000000..7d5531e --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeSearch.cs @@ -0,0 +1,9 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductAttributeSearch : BaseSearch + { + public string Name { get; set; }=string.Empty; + + public long CategoryId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeValueModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeValueModel.cs new file mode 100644 index 0000000..b513822 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductAttributeValueModel.cs @@ -0,0 +1,63 @@ +namespace Atomx.Admin.Client.Models +{ + /// + /// 产品属性值模型 + /// + public class ProductAttributeValueModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 产品ID + /// + public long ProductId { get; set; } + + /// + /// 预设值ID + /// + public long OptionId { get; set; } + + /// + /// 产品属性关系数据ID + /// + public long ProductAttributeRelationId { get; set; } + + /// + /// 属性值名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 属性图片 + /// + public string Image { get; set; } = string.Empty; + + /// + /// 重量 + /// + public decimal Weight { get; set; } + + /// + /// 重量单位,1克,2千克,3磅,4盎司 + /// + public int WeightUnit { get; set; } + + /// + /// 是否是加重的 + /// + public bool IsAddWeight { get; set; } = false; + + /// + /// 展示排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 是否在编辑 + /// + public bool IsEdit { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductDetailModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductDetailModel.cs new file mode 100644 index 0000000..fef5d6f --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductDetailModel.cs @@ -0,0 +1,6 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductDetailModel + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductModel.cs new file mode 100644 index 0000000..e6f9c3e --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductModel.cs @@ -0,0 +1,241 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class ProductModel + { + /// + /// 产品ID + /// + public long Id { get; set; } + + /// + /// 父产品ID + /// + public long ParentProductId { get; set; } + + /// + /// 发布用户ID + /// + + public long UserId { get; set; } + + /// + /// 归属公司ID + /// + + public long CorporationId { get; set; } + + /// + /// 卖家店铺ID + /// + public long StoreId { get; set; } + + /// + /// 产品类型ID + /// + public long ProductTypeId { get; set; } + + /// + /// 分类路径 + /// + public string? CategoryPath { get; set; } = string.Empty; + + /// + /// 分类ID + /// + public long CategoryId { get; set; } + + /// + /// 制造商ID + /// + public long ManufacturerId { get; set; } + + /// + /// SEO Title + /// + public string MetaTitle { get; set; } = string.Empty; + + /// + /// SEO Description + /// + public string MetaDescription { get; set; } = string.Empty; + + /// + /// 产品Slug + /// + public string Slug { get; set; } = string.Empty; + + /// + /// 产品名称 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 产品唯一编码 + /// + public string? SIN { get; set; } = string.Empty; + + /// + /// 封面图片 + /// + public string? Image { get; set; } = string.Empty; + + /// + /// 产品图片,JSON + /// + public string? Photos { get; set; } = string.Empty; + + /// + /// 产品卖点 + /// + public string? Feature { get; set; } = string.Empty; + + /// + /// 产品简介 + /// + public string? Description { get; set; } = string.Empty; + + /// + /// 产品特点标签 + /// + public string? Tags { get; set; } = string.Empty; + + /// + /// 产品详细介绍 + /// + public string Body { get; set; } = string.Empty; + + /// + /// 规格信息Json + /// + public string? Specification { get; set; } = string.Empty; + + /// + /// 商品库存 + /// + public int StockQuantity { get; set; } + + /// + /// 重量 + /// + public decimal? Weight { get; set; } + + /// + /// 市场价,划线价 + /// + public decimal MarketPrice { get; set; } + + /// + /// 销售标价 + /// + public decimal Price { get; set; } + + /// + /// SKU组合的产品价格信息 + /// + public string? SkuPrices { get; set; } = string.Empty; + + /// + /// 商品扩展信息 + /// + public string Extended { get; set; } = string.Empty; + + /// + /// 订单最小数量 + /// + public int OrderMinimumQuantity { get; set; } + + /// + /// 订单最多数量 + /// + public int OrderMaximumQuantity { get; set; } + + /// + /// 是否允许退货 + /// + public bool AllowReturn { get; set; } + + /// + /// 是否允许换货 + /// + public bool AllowExchange { get; set; } + + /// + /// 是否独立发货 + /// + public bool SingleShip { get; set; } + + /// + /// 预计运输天数 + /// + public int ShippingDays { get; set; } + + /// + /// 运费模板ID + /// + public long ShippingId { get; set; } + + /// + /// 精选 + /// + public bool IsFeatured { get; set; } + + /// + /// 是否 best + /// + public bool IsBest { get; set; } + + /// + /// 是否标注新品 + /// + public bool IsNew { get; set; } + + /// + /// 排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 暂停销售,前台处理成已售罄 + /// + public bool StopSales { get; set; } + + /// + /// 产品状态,0未上架,1上架 + /// + + public int Status { get; set; } + + /// + /// 产品信息审核状态 + /// + public int ReviewStatus { get; set; } + + /// + /// 禁用优惠券 + /// + public bool DisableCoupons { get; set; } + + /// + /// 产品属性 + /// + public List ProductSaleAttributes { get; set; } = new(); + + /// + /// 产品属性组合信息 + /// + public List ProductAttributeCombinations { get; set; } = new(); + + /// + /// 用来设置SKU属性图片的数据源 + /// + public List PhotoSalesAttribute { get; set; } = new(); + + /// + /// 是否编辑 + /// + public bool IsEdit { get; set; } = false; + + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductSKUPricesModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSKUPricesModel.cs new file mode 100644 index 0000000..f19ea38 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSKUPricesModel.cs @@ -0,0 +1,23 @@ +namespace Atomx.Admin.Client.Models +{ + /// + /// SKU价格 + /// + public class ProductSKUPricesModel + { + /// + /// SKU组合ID + /// + public long Id { get; set; } + + /// + /// 市场价 + /// + public decimal? MarketPrice { get; set; } + + /// + /// 销售价 + /// + public decimal? Price { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductSaleAttributeModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSaleAttributeModel.cs new file mode 100644 index 0000000..1b7c8d4 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSaleAttributeModel.cs @@ -0,0 +1,68 @@ +namespace Atomx.Admin.Client.Models +{ + /// + /// 产品销售属性模型 + /// + public class ProductSaleAttributeModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 属性ID + /// + public long AttributeId { get; set; } + + /// + /// 控件的类型 + /// + public int ControlType { get; set; } + + /// + /// 必须填重量 + /// + public bool WeightIsRequired { get; set; } + + /// + /// 属性名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 是否拥有规格图片 + /// + public bool Image { get; set; } + + /// + /// 属性排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 产品属性值 + /// + public List ProductAttributeValues { get; set; } = new List(); + + /// + /// + /// + public string SelectOption { get; set; } = string.Empty; + + /// + /// 输入的新属性值 + /// + public string InputText { get; set; } = string.Empty; + + /// + /// 是否编辑状态 + /// + public bool AddAttribute { get; set; } + + /// + /// 是否是必须的属性 + /// + public bool IsRequired { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductSaleSpecificationAttributeModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSaleSpecificationAttributeModel.cs new file mode 100644 index 0000000..b17ccd3 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSaleSpecificationAttributeModel.cs @@ -0,0 +1,6 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductSaleSpecificationAttributeModel + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSearch.cs new file mode 100644 index 0000000..ef8d2c3 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSearch.cs @@ -0,0 +1,25 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductSearch : BaseSearch + { + /// + /// 产品标题 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 分类ID + /// + public string CategoryId { get; set; } = string.Empty; + + /// + /// 产品状态 + /// + public string Status { get; set; } = string.Empty; + + /// + /// 开始时间 + /// + public DateTime?[] RangeTime { get; set; } = new DateTime?[] { null, null }; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductSkuModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSkuModel.cs new file mode 100644 index 0000000..114e32d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductSkuModel.cs @@ -0,0 +1,77 @@ +using Atomx.Utils.Extension; + +namespace Atomx.Admin.Client.Models +{ + /// + /// 产品SKU信息模型 + /// + public class ProductSkuModel : IEquatable + { + /// + /// 属性ID + /// + public long AttributeId { get; set; } + + /// + /// 属性名称 + /// + public string AttributeName { get; set; } = string.Empty; + + /// + /// 属性值ID + /// + public long ValueId { get; set; } + + /// + /// 属性值 Name + /// + public string ValueName { get; set; } = string.Empty; + + /// + /// 重量 + /// + public decimal Weight { get; set; } + + /// + /// 重量单位,1毫克mg,2克g,3千克kg,4吨t,5磅lb,6盎司oz + /// + public int WeightUnit { get; set; } + + public bool Equals(ProductSkuModel? other) + { + if (other is null) return false; + if (ValueId != other.ValueId) + { + return AttributeId == other.AttributeId && + ValueId == other.ValueId && + Weight.RemoveTrailingZeros() == other.Weight.RemoveTrailingZeros() && + WeightUnit == other.WeightUnit && + AttributeName == other.AttributeName && + ValueName == other.ValueName; + } + else + { + return AttributeId == other.AttributeId && + ValueId == other.ValueId && + AttributeName == other.AttributeName; + } + } + + public override bool Equals(object obj) => Equals(obj as ProductSkuModel); + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = hash * 23 + AttributeId.GetHashCode(); + hash = hash * 23 + ValueId.GetHashCode(); + hash = hash * 23 + Weight.GetHashCode(); + hash = hash * 23 + WeightUnit.GetHashCode(); + hash = hash * 23 + (AttributeName?.GetHashCode() ?? 0); + hash = hash * 23 + (ValueName?.GetHashCode() ?? 0); + return hash; + } + } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockAttributeModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockAttributeModel.cs new file mode 100644 index 0000000..db22f47 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockAttributeModel.cs @@ -0,0 +1,75 @@ +namespace Atomx.Admin.Client.Models +{ + public class ProductStockAttributeModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 属性ID + /// + public long AttributeId { get; set; } + + /// + /// 控件的类型 + /// + public int ControlType { get; set; } + + /// + /// 必须填重量 + /// + public bool WeightIsRequired { get; set; } + + /// + /// 重量单位,1毫克mg,2克g,3千克kg,4吨t,5磅lb,6盎司oz + /// + public int WeightUnit { get; set; } + + /// + /// 属性名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 是否拥有规格图片 + /// + public bool Image { get; set; } + + /// + /// 属性排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 产品属性值 + /// + public List ProductAttributeValues { get; set; } = new List(); + + /// + /// 新产品属性值 + /// + public ProductAttributeValueModel AttributeValue { get; set; } = new ProductAttributeValueModel(); + + /// + /// + /// + public string SelectOption { get; set; } = string.Empty; + + /// + /// 输入的新属性值 + /// + public string InputText { get; set; } = string.Empty; + + /// + /// 是否编辑状态 + /// + public bool AddAttribute { get; set; } + + /// + /// 是否是必须的属性 + /// + public bool IsRequired { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockModel.cs new file mode 100644 index 0000000..0384179 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockModel.cs @@ -0,0 +1,32 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class ProductStockModel: ProductAttributeCombination + { + /// + /// 产品名称 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 封面图片 + /// + public string? Image { get; set; } = string.Empty; + + /// + /// 产品SKU信息 + /// + public List Sku { get; set; } = new(); + + /// + /// 仓库ID + /// + public long WarehouseId { get; set; } + + /// + /// 添加库存 + /// + public int AddStockQuantity { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockUnitModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockUnitModel.cs new file mode 100644 index 0000000..be12c38 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockUnitModel.cs @@ -0,0 +1,22 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + /// + /// 产品SKU库存量模型 + /// + public class ProductStockUnitModel : ProductAttributeCombination + { + /// + /// 产品名称 + /// + public string Title { get; set; } = string.Empty; + + /// + /// 封面图片 + /// + public string? Image { get; set; } = string.Empty; + + public List Sku { get; set; } = new(); + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockUnitSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockUnitSearch.cs new file mode 100644 index 0000000..3f83b6e --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductStockUnitSearch.cs @@ -0,0 +1,23 @@ +namespace Atomx.Admin.Client.Models +{ + /// + /// 产品SKU库存量搜索模型 + /// + public class ProductStockUnitSearch : BaseSearch + { + /// + /// 产品ID + /// + public int Type { get; set; } + + /// + /// 产品名称 + /// + public string Key { get; set; } = string.Empty; + + /// + /// 重量 + /// + public decimal Weight { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/ProductTransferSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/ProductTransferSearch.cs new file mode 100644 index 0000000..9dd9503 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/ProductTransferSearch.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class ProductTransferSearch : BaseSearch + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialBatchSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialBatchSearch.cs new file mode 100644 index 0000000..3313f07 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialBatchSearch.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class RawMaterialBatchSearch : BaseSearch + { + /// + /// 原料类型ID + /// + public long MaterialId { get; set; } + + /// + /// 原料归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 原料归属店铺网点ID + /// + public long StoreId { get; set; } + } +} + \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialRecordSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialRecordSearch.cs new file mode 100644 index 0000000..091c882 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialRecordSearch.cs @@ -0,0 +1,20 @@ +namespace Atomx.Admin.Client.Models +{ + public class RawMaterialRecordSearch : BaseSearch + { + /// + /// 原料类型ID + /// + public long MaterialId { get; set; } + + /// + /// 原料归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 原料归属店铺网点ID + /// + public long StoreId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialSearch.cs new file mode 100644 index 0000000..50ee780 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialSearch.cs @@ -0,0 +1,22 @@ +namespace Atomx.Admin.Client.Models +{ + public class RawMaterialSearch : BaseSearch + { + /// + /// 原料类型ID + /// + public long MaterialId { get; set; } + + /// + /// 原料归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 原料归属店铺网点ID + /// + public long StoreId { get; set; } + + + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialTransferSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialTransferSearch.cs new file mode 100644 index 0000000..0bbddcd --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RawMaterialTransferSearch.cs @@ -0,0 +1,25 @@ +namespace Atomx.Admin.Client.Models +{ + public class RawMaterialTransferSearch : BaseSearch + { + /// + /// 原料类型ID + /// + public long MaterialId { get; set; } + + /// + /// 原料归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 原料归属店铺网点ID + /// + public long FromStoreId { get; set; } + + /// + /// 原料归属店铺网点ID + /// + public long ToStoreId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RoleModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RoleModel.cs new file mode 100644 index 0000000..8e63892 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RoleModel.cs @@ -0,0 +1,47 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Atomx.Admin.Client.Models +{ + public class RoleModel + { + /// + /// 数据ID + /// + public int Id { get; set; } + + /// + /// 角色类型 + /// + public int Type { get; set; } + + /// + /// 角色名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 说明 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 权限 + /// + public string Permission { get; set; } = string.Empty; + + /// + /// 是否系统角色 + /// + public bool IsSystemRole { get; set; } + + /// + /// 是否可用 + /// + public bool Enabled { get; set; } + + /// + /// 是否是编辑 + /// + public bool IsEdit { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RolePermissionGroup.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RolePermissionGroup.cs new file mode 100644 index 0000000..cbf593d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RolePermissionGroup.cs @@ -0,0 +1,11 @@ +namespace Atomx.Admin.Client.Models +{ + public class RolePermissionGroup + { + public string Category { get; set; } + + public string CategoryName { get; set; } + + public List PermissionItems { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RolePermissionItem.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RolePermissionItem.cs new file mode 100644 index 0000000..b91b11f --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RolePermissionItem.cs @@ -0,0 +1,9 @@ +namespace Atomx.Admin.Client.Models +{ + public class RolePermissionItem + { + public string Name { get; set; } + public string Description { get; set; } + public bool IsSelected { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/RoleSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/RoleSearch.cs new file mode 100644 index 0000000..7e02acb --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/RoleSearch.cs @@ -0,0 +1,7 @@ +namespace Atomx.Admin.Client.Models +{ + public class RoleSearch + { + public string? Name { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SiteAppModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SiteAppModel.cs new file mode 100644 index 0000000..ac11c6a --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SiteAppModel.cs @@ -0,0 +1,26 @@ +namespace Atomx.Admin.Client.Models +{ + public class SiteAppModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 网站应用类型 + /// + public int Type { get; set; } + + + /// + /// 网站应用名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 是否可用 + /// + public bool Enabled { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SiteAppSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SiteAppSearch.cs new file mode 100644 index 0000000..f671236 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SiteAppSearch.cs @@ -0,0 +1,9 @@ +namespace Atomx.Admin.Client.Models +{ + public class SiteAppSearch : BaseSearch + { + public int? Type { get; set; } + + public string Name { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeItemModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeItemModel.cs new file mode 100644 index 0000000..8fbe647 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeItemModel.cs @@ -0,0 +1,9 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class SpecificationAttributeItemModel:SpecificationAttribute + { + public string CategoryName { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeModel.cs new file mode 100644 index 0000000..65c9d07 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeModel.cs @@ -0,0 +1,47 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace Atomx.Admin.Client.Models +{ + public class SpecificationAttributeModel + { + /// + /// 属性ID + /// + [DatabaseGenerated(DatabaseGeneratedOption.None)] + [Key] + public long Id { get; set; } + + /// + /// 上级属性,用于规格属性做分组 + /// + public long ParentId { get; set; } + + /// + /// 分类ID + /// + public long CategoryId { get; set; } + + /// + /// 属性名称 + /// + [Column(TypeName = "varchar(50)")] + + public string Name { get; set; } = string.Empty; + + /// + /// 属性控件类型,1选项,2文本 + /// + public int ControlType { get; set; } + + /// + /// 展示排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 数据状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeOptionModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeOptionModel.cs new file mode 100644 index 0000000..df1fef5 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeOptionModel.cs @@ -0,0 +1,47 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace Atomx.Admin.Client.Models +{ + public class SpecificationAttributeOptionModel + { + /// + /// 预设值ID + /// + [DatabaseGenerated(DatabaseGeneratedOption.None)] + [Key] + + public long Id { get; set; } + + /// + /// 供应商自定义值 + /// + public long VendorId { get; set; } + + /// + /// 产品属性ID + /// + public long SpecificationId { get; set; } + + /// + /// 预设值名称 + /// + [Column(TypeName = "varchar(50)")] + public string Value { get; set; } = string.Empty; + + /// + /// 展示排序 + /// + public int DisplayOrder { get; set; } + + /// + /// 是否通用的 + /// + public bool Standard { get; set; } + + /// + /// 数据状态 + /// + public int Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeOptionSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeOptionSearch.cs new file mode 100644 index 0000000..65b4d75 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeOptionSearch.cs @@ -0,0 +1,15 @@ +namespace Atomx.Admin.Client.Models +{ + public class SpecificationAttributeOptionSearch:BaseSearch + { + /// + /// 值 + /// + public string? Value { get; set; } + + /// + /// 规格ID + /// + public long SpecificationId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeSearch.cs new file mode 100644 index 0000000..9f5a654 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/SpecificationAttributeSearch.cs @@ -0,0 +1,12 @@ +namespace Atomx.Admin.Client.Models +{ + public class SpecificationAttributeSearch : BaseSearch + { + /// + /// 名称 + /// + public string? Name { get; set; } + + public long CategoryId { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/StoreProductItemSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/StoreProductItemSearch.cs new file mode 100644 index 0000000..3e66944 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/StoreProductItemSearch.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class StoreProductItemSearch + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/StoreSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/StoreSearch.cs new file mode 100644 index 0000000..ec9e56a --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/StoreSearch.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class StoreSearch : BaseSearch + { + + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/UploadFileModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/UploadFileModel.cs new file mode 100644 index 0000000..4fee5b6 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/UploadFileModel.cs @@ -0,0 +1,42 @@ +namespace Atomx.Admin.Client.Models +{ + public class UploadFileModel + { + public long Id { get; set; } + + /// + /// 文件类型 + /// + public int Type { get; set; } + + /// + /// 文件名 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 存放路径 + /// + public string Path { get; set; } = string.Empty; + + /// + /// 文件扩展 + /// + public string Extension { get; set; } = string.Empty; + + /// + /// 文件扩展 + /// + public string ContentType { get; set; } = string.Empty; + + /// + /// 创建用户ID + /// + public long CreateUid { get; set; } + + /// + /// 创建用户名 + /// + public string CreateBy { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/UploadFileSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/UploadFileSearch.cs new file mode 100644 index 0000000..b43651d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/UploadFileSearch.cs @@ -0,0 +1,20 @@ +namespace Atomx.Admin.Client.Models +{ + public class UploadFileSearch : BaseSearch + { + /// + /// 文件类型 + /// + public int? Type { get; set; } + + /// + /// 名称 + /// + public string? Name { get; set; } + + /// + /// 开始时间 + /// + public DateTime?[] RangeTime { get; set; } = new DateTime?[] { null, null }; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/UserSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/UserSearch.cs new file mode 100644 index 0000000..415f3ab --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/UserSearch.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Atomx.Admin.Client.Models +{ + public class UserSearch : BaseSearch + { + /// + /// 用户名或昵称 + /// + public string? Username { get; set; } + + /// + /// 邮件地址 + /// + public string? Email { get; set; } + + /// + /// 手机号码 + /// + public string? Mobile { get; set; } + + /// + /// 真实姓名 + /// + public string? RealName { get; set; } + + /// + /// 开始时间 + /// + public DateTime?[] RangeTime { get; set; } = new DateTime?[] { null, null }; + + /// + /// 状态 + /// + public string? Status { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseItemModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseItemModel.cs new file mode 100644 index 0000000..012de5c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseItemModel.cs @@ -0,0 +1,12 @@ +using Atomx.Common.Entities; + +namespace Atomx.Admin.Client.Models +{ + public class WarehouseItemModel:Warehouse + { + /// + /// 公司名称 + /// + public string CorporationName { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseModel.cs b/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseModel.cs new file mode 100644 index 0000000..8ee100c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseModel.cs @@ -0,0 +1,28 @@ +namespace Atomx.Admin.Client.Models +{ + /// + /// 仓库模型 + /// + public class WarehouseModel + { + /// + /// 数据ID + /// + public long Id { get; set; } + + /// + /// 归属公司ID + /// + public long CorporationId { get; set; } + + /// + /// 分类名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 是否可用 + /// + public bool Enabled { get; set; } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseSearch.cs b/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseSearch.cs new file mode 100644 index 0000000..7fcd66c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Models/WarehouseSearch.cs @@ -0,0 +1,20 @@ +namespace Atomx.Admin.Client.Models +{ + public class WarehouseSearch : BaseSearch + { + /// + /// 仓库名称 + /// + public string? Name { get; set; } = string.Empty; + + /// + /// 企业ID + /// + public long CorporationId { get; set; } = 0; + + /// + /// 企业名称 + /// + public string CorporationName { get; set; } = string.Empty; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/ContentEdit.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/ContentEdit.razor new file mode 100644 index 0000000..9d66945 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/ContentEdit.razor @@ -0,0 +1,5 @@ +ContentEdit + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/ContentList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/ContentList.razor new file mode 100644 index 0000000..071d3be --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/ContentList.razor @@ -0,0 +1,7 @@ +@page "/content/list" + +ContentList + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/PageEdit.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/PageEdit.razor new file mode 100644 index 0000000..18a25c3 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/PageEdit.razor @@ -0,0 +1,5 @@ +PageEdit + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/PageList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/PageList.razor new file mode 100644 index 0000000..d2addaa --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Contents/PageList.razor @@ -0,0 +1,8 @@ +@page "/content/page/list" + + +PageList + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Counter.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Counter.razor new file mode 100644 index 0000000..ef23cb3 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Counter.razor @@ -0,0 +1,18 @@ +@page "/counter" + +Counter + +Counter + +Current count: @currentCount + +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Home.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Home.razor new file mode 100644 index 0000000..3a8532b --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Home.razor @@ -0,0 +1,24 @@ +@page "/" +@attribute [Authorize] + +Home + +Hello, world! + +Welcome to your new app. + + + 产品分类 + + + 产品分类编辑 + + + 多语言设置 + + + 多语言资源设置 + + + 角色管理 + \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Login.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Login.razor new file mode 100644 index 0000000..64a6bbd --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Login.razor @@ -0,0 +1,140 @@ +@page "/account/login" +@layout EmptyLayout +@inject ILogger Logger + + +登录 + +@if (!dataLoaded) +{ + +} +else +{ + + + + + + + + + + + + + + + + + + + 忘记密码 + + + 马上注册 + + + + 登录 + + + + + + + Copyright © 2025-@DateTime.Now.Year Atomx.cn All rights reserved. + runing as @handler + + +} + + +@code { + string handler = "Server"; + + [Parameter] + [SupplyParameterFromQuery(Name = "ReturnUrl")] + public string? ReturnUrl { get; set; } + + [SupplyParameterFromForm] + public LoginModel login { get; set; } = new(); + private Form form = null!; + + bool dataLoaded = false; + + string message { get; set; } = string.Empty; + + private bool _isLoading = false; + + + protected override void OnInitialized() + { + if (OperatingSystem.IsBrowser()) + { + handler = "Wasm"; + } + else + { + handler = "Server"; + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity != null) + { + if (authState.User.Identity.IsAuthenticated) + { + Navigation.NavigateTo(ReturnUrl ?? "/"); + } + } + } + if (!dataLoaded) + { + dataLoaded = true; + StateHasChanged(); + } + } + + private async Task LoginAsync() + { + if (form.Validate()) + { + var api = "/api/sign/in"; + var result = await HttpService.Post>(api, login); + if (result.Success) + { + Console.WriteLine("请求api成功"); + if (!string.IsNullOrEmpty(result.Data)) + { + await localStorage.SetItemAsStringAsync(StorageKeys.JWTTokenKeyName, result.Data); + await localStorage.SetItemAsStringAsync("refreshToken", result.Data); + var authState = (AuthStateProvider as PersistentAuthenticationStateProvider); + if (authState != null) + { + authState.UpdateAuthenticationState(result.Data); + } + Logger.LogInformation($"登录成功跳转目标,{ReturnUrl}"); + Navigation.NavigateTo(ReturnUrl ?? "/"); + } + } + else + { + ModalService.Error(new ConfirmOptions() { Title = "提示", Content = result.Message }); + } + } + + } + + private async Task OnPasswordKeyDown(KeyboardEventArgs value) + { + if (value.Key == "Enter") + { + await LoginAsync(); + } + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Logout.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Logout.razor new file mode 100644 index 0000000..fc075be --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Logout.razor @@ -0,0 +1,11 @@ +@page "/logout" +@layout EmptyLayout + +@code { + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await ((PersistentAuthenticationStateProvider)AuthStateProvider).MarkUserAsLoggedOut(); + Navigation.NavigateTo("/account/login"); + await base.OnAfterRenderAsync(firstRender); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Orders/OrderList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Orders/OrderList.razor new file mode 100644 index 0000000..4a7cfe9 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Orders/OrderList.razor @@ -0,0 +1,5 @@ +OrderList + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Orders/OrderView.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Orders/OrderView.razor new file mode 100644 index 0000000..d6b7f09 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Orders/OrderView.razor @@ -0,0 +1,7 @@ +@page "/orders/view/{OrderId:long}" + +OrderView + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/CategoryEdit.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/CategoryEdit.razor new file mode 100644 index 0000000..b9e96bf --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/CategoryEdit.razor @@ -0,0 +1,137 @@ +@page "/product/category/edit" +@page "/product/category/edit/{Id:long?}" + +@inject ILogger Logger +@* @attribute [Authorize] *@ + +分类编辑 +分类信息 + + + + + @* @if (languages.Count > 1 && Id > 0) + { + + + + 标准 + + + @foreach (var item in languages) + { + + + @item.Name @item.Id.ToString() + + + } + + } + + + + + @foreach (var item in categories) + { + + } + + + + + + + + + + + + @if (string.IsNullOrEmpty(formModel.Image)) + { + 选择图标 + } + else + { + 已选图标: + } + + *@ + + + + + + + + + + + 启用 + + + + 提交保存 + + + + + + +@code { + [SupplyParameterFromQuery] + int? Lang { get; set; } + + [Parameter] + public long? Id { get; set; } + + [SupplyParameterFromForm] + CategoryModel model { get; set; } = new(); + Form editform = null!; + + bool pageLoading = true; + List Categories = new(); + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override async Task OnParametersSetAsync() + { + pageLoading = true; + // await LoadCategories(); + // if (Id > 0) + // { + // await LoadData(); + // await SearchAttribute(string.Empty); + // } + // else + // { + // editCategory = true; + // CategoryPath = string.Empty; + // } + // if (Categories.Any()) + // { + // BindCategory(); + // } + pageLoading = false; + base.OnParametersSet(); + } + + async Task LoadCategories() + { + var url = $"/api/category/items"; + var apiResult = await HttpService.Get>>(url); + if (apiResult.Success) + { + Categories = apiResult.Data ?? new(); + } + } + + async void OnFormFinishAsync() + { + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/CategoryList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/CategoryList.razor new file mode 100644 index 0000000..f169d83 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/CategoryList.razor @@ -0,0 +1,336 @@ +@page "/category/list" + +@inject ILogger Logger +@attribute [Authorize] + +分类管理 +菜单管理 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + 菜单列表 + + 新增 + + + + + + + @GetName(context) + + + + + + + @if (context.Enabled) + { + + + } + else + { + + } + + + + + @if (context.IsLast) + { + + 属性 + + + + 规格 + + + } + + + + + + + + OnEditClick(context)"> 编辑 + + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @foreach (var item in categories) + { + + } + + + + + + + + + 启用 + + + 保存 + + + + +@code { + + + + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromForm] + CategorySearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + CategoryModel model { get; set; } = new(); + Form editform = null!; + + List categories = new(); + + bool drawerVisible; + + protected override void OnParametersSet() + { + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Name = query.GetQueryString("Name"); + } + + private async void LoadList() + { + var url = "/api/category/list"; + var apiResult = await HttpService.Get>>(url); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + categories = apiResult.Data; + StateHasChanged(); + } + } + } + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + void OnSearchReset() + { + search = new(); + searchForm?.Reset(); + OnSearchFinish(); + } + + private void OnReset() + { + search = new(); + LoadList(); + + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/category/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/category/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/category/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/category/list?{queryString}"); + } + } + LoadList(); + } + + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + + var url = $"/api/category/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + void OnEditClick(CategoryItem item) + { + this.model = item.Adapt(); + drawerVisible = true; + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + var result = new ApiResult(); + var data = model.Adapt(); + if (model.Id > 0) + { + var url = $"api/category/edit"; + result = await HttpService.Post>(url, data); + } + else + { + var url = $"api/category/add"; + result = await HttpService.Post>(url, data); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + private string GetPath(string path) + { + var array = path.Split(",").ToList(); + var pathText = string.Empty; + foreach (var item in array) + { + var name = categories.Where(p => p.Id == item.ToLong()).Select(p => p.Name).SingleOrDefault(); + if (string.IsNullOrEmpty(pathText)) + { + pathText = name; + } + else + { + pathText = $"{pathText}>{name}"; + } + } + return pathText; + } + + private string GetName(CategoryItem menu) + { + string name = string.Empty; + if (menu.Depth > 0) + { + var symbol = " "; + for (var i = 0; i < menu.Depth; i++) + { + symbol = $"{symbol}{symbol}"; + } + name = $"{symbol}├ {menu.Name}"; + } + else + { + name = menu.Name; + } + return name; + } + + void OnCreateClick() + { + model = new CategoryModel(); + drawerVisible = true; + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/GoodsList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/GoodsList.razor new file mode 100644 index 0000000..6e162c2 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/GoodsList.razor @@ -0,0 +1,5 @@ +GoodsList + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/PredefinedAttributeList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/PredefinedAttributeList.razor new file mode 100644 index 0000000..1633a9b --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/PredefinedAttributeList.razor @@ -0,0 +1,290 @@ +@page "/attribute/predefined/list/{AttributeId:long}" + +@inject ILogger Logger +@attribute [Authorize] + + +产品属性管理 +属性列表 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + @productAttribute.Name 属性预设值列表 + + 新增 + + + + + + + + @if (context.Status == 1) + { + + + } + else + { + + } + + + + + + OnEditClick(context)">编辑 + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + 启用 + 禁用 + + + + 保存 + + + + +@code { + [Parameter] + public long AttributeId { get; set; } + + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + ProductAttributeOptionSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + ProductAttributeOptionModel model { get; set; } = new(); + Form editform = null!; + + ProductAttribute productAttribute = new(); + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + + + bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + LoadAttribute(); + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Value = query.GetQueryString("Value"); + } + + private async void LoadAttribute() + { + var url = $"/api/productattribute/{AttributeId}"; + var apiResult = await HttpService.Get>(url); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + productAttribute = apiResult.Data; + } + } + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/productattributeoption/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + + private void OnSearchReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/attribute/predefined/list/{AttributeId}?page={page}"); + } + else + { + Navigation.NavigateTo($"/attribute/predefined/list/{AttributeId}"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/attribute/predefined/list/{AttributeId}?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/attribute/predefined/list/{AttributeId}?{queryString}"); + } + } + } + + void OnPageChanged(PaginationEventArgs args) + { + OnSearch(args.Page); + } + + + + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + + var url = $"/api/productattributeoption/delete/{model.Id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + + var result = new ApiResult(); + if (model.Id > 0) + { + var url = $"api/productattributeoption/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/productattributeoption/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + + void OnEditClick(ProductAttributeOption option) + { + this.model = option.Adapt(); + drawerVisible = true; + } + + void OnCreateClick() + { + model = new(); + drawerVisible = true; + } + + private void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/PredefinedSpecificationList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/PredefinedSpecificationList.razor new file mode 100644 index 0000000..5c2c0f1 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/PredefinedSpecificationList.razor @@ -0,0 +1,325 @@ +@page "/specification/predefined/list/{SpecificationId:long}" + +@inject ILogger Logger +@attribute [Authorize] + + +产品规格管理 + +属性列表 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + @specificationAttribute.Name 属性列表 + + 新增 + + + + + + + + @if (context.Status == 1) + { + + + } + else + { + + } + + + + + + OnEditClick(context)">编辑 + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + 启用 + 禁用 + + + + 保存 + + + + +@code { + [Parameter] + public long SpecificationId { get; set; } + + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + SpecificationAttributeOptionSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + SpecificationAttributeOptionModel model { get; set; } = new(); + Form editform = null!; + + SpecificationAttribute specificationAttribute = new(); + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + private bool drawerVisible; + + SpecificationAttributeOptionModelValidator validator = new(); + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + LoadSpecification(); + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Value = query.GetQueryString("Value"); + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/specificationattributeoption/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + private async void LoadSpecification() + { + var url = $"/api/specificationattribute/{SpecificationId}"; + var apiResult = await HttpService.Get>(url); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + specificationAttribute = apiResult.Data; + } + } + } + + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + + private void OnSearchReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/specification/predefined/list/{SpecificationId}?page={page}"); + } + else + { + Navigation.NavigateTo($"/specification/predefined/list/{SpecificationId}"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/specification/predefined/list/{SpecificationId}?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/specification/predefined/list/{SpecificationId}?{queryString}"); + } + } + // LoadList(); + } + + void OnPageChanged(PaginationEventArgs args) + { + + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + Navigation.NavigateTo($"/attribute/list?page={args.Page}"); + } + else + { + Navigation.NavigateTo($"/attribute/list?page={Page}&{queryString}"); + } + } + + + + + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + + var url = $"/api/specificationattributeoption/delete/{model.Id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + model.SpecificationId = SpecificationId; + if (editform.Validate()) + { + + + var result = new ApiResult(); + if (model.Id > 0) + { + var url = $"api/specificationattributeoption/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/specificationattributeoption/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + private void OnPageChanged(int args) + { + OnSearch(args); + } + + private bool _editDrawerOpen; + + private void OpenDrawer() + { + model = new(); + _editDrawerOpen = true; + } + + void OnEditClick(SpecificationAttributeOption item) + { + this.model = item.Adapt(); + drawerVisible = true; + } + + void OnDrawerOverlayClick(bool state) + { + if (!state) + { + model = new(); + _editDrawerOpen = state; + } + } + + void OnCreateClick() + { + drawerVisible = true; + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductAttributeList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductAttributeList.razor new file mode 100644 index 0000000..ae00267 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductAttributeList.razor @@ -0,0 +1,339 @@ +@page "/attribute/list" + +@rendermode InteractiveAuto +@inject ILogger Logger +@inject HttpService HttpService +@attribute [Authorize] + + +产品属性管理 + +属性列表 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + 属性列表 + + 新增 + + + + + + + @if (context.ControlType == 1) + { + 选项 + } + else + { + 文本框 + } + + + + @if (context.WeightIsRequired) + { + 必填 + } + + + @if (context.IsRequired) + { + 必选 + } + + + + @if (context.Status == 1) + { + + + } + else + { + + } + + + + + + OnViewPredefinedListClick(context.Id)">预设值 + + + OnEditClick(context)">编辑 + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + 默认(文本) + 选项 + + + + 必填 + + + 默认属性 + + + + 启用 + 禁用 + + + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery] + long CategoryId { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + ProductAttributeSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + ProductAttributeModel model { get; set; } = new(); + Form editform = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + private bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + model.CategoryId = CategoryId; + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Name = query.GetQueryString("Name"); + search.CategoryId = CategoryId; + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/productattribute/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + + private void OnSearchReset() + { + search = new(); + LoadList(); + } + + private void OnReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/attribute/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/attribute/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/attribute/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/attribute/list?{queryString}"); + } + } + } + + void OnPageChanged(PaginationEventArgs args) + { + + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + Navigation.NavigateTo($"/attribute/list?page={args.Page}"); + } + else + { + Navigation.NavigateTo($"/attribute/list?page={Page}&{queryString}"); + } + } + + + void OnEditClick(ProductAttribute productAttribute) + { + this.model = productAttribute.Adapt(); + drawerVisible = true; + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/productattribute/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + Console.WriteLine("run on from finish "); + if (CategoryId > 0) + { + model.CategoryId = CategoryId; + } + + if (editform.Validate()) + { + var result = new ApiResult(); + if (model.Id > 0) + { + var url = $"api/productattribute/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/productattribute/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + private void OnPageChanged(int args) + { + OnSearch(args); + } + + + void OnCreateClick() + { + drawerVisible = true; + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } + + void OnViewPredefinedListClick(long id) + { + Navigation.NavigateTo($"/attribute/Predefined/list/{id}"); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductEdit.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductEdit.razor new file mode 100644 index 0000000..46c40a5 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductEdit.razor @@ -0,0 +1,1201 @@ +@page "/product/create" +@page "/product/edit/{id:long?}" + +@inject ILogger Logger +@attribute [Authorize] + +编辑产品 + + 编辑产品信息 + + + + + 商品类目 + + + 标题内容需包含商品名称,建议添加材质、功能、特征或商品描述 + + + + + + @if (editCategory) + { + + + + + + @foreach (var item in Level1Categories) + { + + } + + + @if (showLevel2Category) + { + + + @foreach (var item in Level2Categories) + { + + } + + + + } + @if (showLevel3Category) + { + + + @foreach (var item in Level3Categories) + { + + } + + + + } + @if (showLevel4Category) + { + + + @foreach (var item in Level4Categories) + { + + } + + + + } + + + + 下一步 + + } + else + { + + @CategoryPath修改 + + + + + + + + + + + + + + @if (model.ProductSaleAttributes.Count > 0) + { + + @foreach (var item in model.ProductSaleAttributes) + { + if (item.AttributeId > 0) + { + + + + + @item.Name@item.DisplayOrder + + + 下移 + 上移 + @if (!item.IsRequired) + { + + } + + + + @foreach (var value in item.ProductAttributeValues) + { + @if (item.ControlType == 0) + { + @if (value.IsEdit) + { + + + @if (CurrentProductAttribute.WeightIsRequired) + { + + @GetWeightUnit(CurrentProductAttribute.WeightUnit) + + } + 确认 + 取消 + + } + else + { + + + @if (item.WeightIsRequired) + { + + 克 + + } + @value.DisplayOrder + + + + + + + + } + } + else + { + + } + } + @if (item.AddAttribute) + { + + + @if (CurrentProductAttribute.WeightIsRequired) + { + + @GetWeightUnit(CurrentProductAttribute.WeightUnit) + + } + 确认 + 取消 + + } + else + { + + 新增@(item.Name)值 + + } + + + + } + else + { + + + + + + + + + + + + + 下移 + 上移 + + + + + + } + } + + } + @if (model.ProductSaleAttributes.Count < 3) + { + + 添加规格属性类型(@(model.ProductSaleAttributes.Count)/3) + + } + + @if (ProductAttributeCombinationsForEdit.Count() > 0) + { + + + + + @foreach (var item in model.ProductSaleAttributes) + { + + } + + + + + + + + + @foreach (var item in model.ProductSaleAttributes) + { + if (item.ProductAttributeValues.Any()) + { + + @item.Name + + } + } + 上架 + + 市场价 + 销售价 + 库存 + 规格编码 + + + + @foreach (var item in ProductAttributeCombinationsForEdit) + { + + @foreach (var sku in item.SkuAttributes) + { + + @sku.ValueName + @if (sku.Weight > 0) + { + (@($"{sku.Weight}{@GetWeightUnit(sku.WeightUnit)}")) + } + + + } + + + + + + + + + + + + + + + + + + } + + + + + } + + + + + + 发布 + 保存草稿 + + + + 保存 + + } + + + + + +@code { + [Parameter] + public long Id { get; set; } + + /// + /// 产品表单model + /// + [SupplyParameterFromForm] + ProductModel model { get; set; } = new(); + Form editForm = null!; + + /// + /// 分类下可用的产品属性集 + /// + List productAttributes = new(); + /// + /// 当前编辑中的产品属性 + /// + ProductAttribute CurrentProductAttribute = new(); + + bool editCategory; + bool loading = false; + + /// + /// 当前编辑中的属性值对象 + /// + ProductAttributeValueModel editAttributeValue = new(); + + /// + /// 分类信息 + /// + List Categories = new(); + CategoryItem? Category = new(); + List Level1Categories = new(); + long level1CategoryId; + List Level2Categories = new(); + long level2CategoryId; + bool showLevel2Category; + List Level3Categories = new(); + long level3CategoryId; + bool showLevel3Category; + List Level4Categories = new(); + long level4CategoryId; + bool showLevel4Category; + + + string CategoryPath = string.Empty; + + ProductSaleAttributeModel editAttribute = new(); + List attributeOptions = new(); + + List SpecificationAttributes = new(); //分类规则属性 + + /// + /// 编辑的属性组合SKU信息 + /// + List ProductAttributeCombinationsForEdit = new(); + + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override async Task OnParametersSetAsync() + { + loading = true; + await LoadCategories(); + if (Id > 0) + { + await LoadData(); + await SearchAttribute(string.Empty); + } + else + { + editCategory = true; + CategoryPath = string.Empty; + } + if (Categories.Any()) + { + BindCategory(); + } + loading = false; + base.OnParametersSet(); + } + + + + async Task LoadData() + { + var url = $"/api/product/detail/{Id}"; + var apiResult = await HttpService.Get>(url); + if (apiResult.Success) + { + if (apiResult.Data == null) + { + Navigation.NavigateTo($"/product/list"); + } + else + { + model = apiResult.Data.Adapt(); + model.IsEdit = true; + RebuildSkuCombinations(); + } + } + else + { + Navigation.NavigateTo($"/product/list"); + } + + StateHasChanged(); + } + + + async void OnFormFinish() + { + if (editForm.Validate()) + { + + var result = new ApiResult(); + model.ProductAttributeCombinations = ProductAttributeCombinationsForEdit; + + Console.WriteLine("提交数据:"); + Console.WriteLine(model.ToJson()); + + if (model.Id > 0) + { + var url = $"api/product/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/product/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "数据提交成功" }); + Navigation.NavigateTo($"/product/list"); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = result.Message }); + } + } + } + + void OnClickEditCategory() + { + ModalService.Confirm(new ConfirmOptions() + { + + Title = "操作提示", + Content = "修改类目可能会导致部分内容丢失,确认修改?", + OkText = "修改类目", + OnOk = (e) => { editCategory = true; CategoryPath = string.Empty; StateHasChanged(); return Task.CompletedTask; }, + CancelText = "取消" + }); + } + + void OnAddAttributeClick() + { + int count = model.ProductSaleAttributes.Count(); + model.ProductSaleAttributes.Add(new ProductSaleAttributeModel() { DisplayOrder = count + 1 }); + } + + async Task SearchAttribute(string value) + { + ProductAttributeSearch attributeSearch = new() + { + Name = value, + CategoryId = model.CategoryId + }; + + var url = $"/api/productattribute/search"; + var apiResult = await HttpService.GetPagingList(url, attributeSearch, 1, 50); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + productAttributes = apiResult.Data.Items; + + if (Id == 0) + { + + var requiredAttributes = productAttributes.Where(p => p.IsRequired).ToList(); + foreach (var item in requiredAttributes) + { + + var attribute = new ProductSaleAttributeModel() + { + AttributeId = item.Id, + Name = item.Name, + ControlType = item.ControlType, + WeightIsRequired = item.WeightIsRequired, + IsRequired = item.IsRequired + }; + + model.ProductSaleAttributes.Add(attribute); + } + } + return; + } + } + productAttributes = new List(); + } + + /// + /// 在Select中选中了要添加的属性对象 + /// + /// + /// + void ProductAttributeChanged(AutoCompleteOption item, ProductSaleAttributeModel saleAttributeModel) + { + Console.WriteLine($"选中了{item.Value}"); + var id = (long)item.Value; + var attribute = productAttributes.FirstOrDefault(x => x.Id == id); + if (attribute != null) + { + if (model.ProductSaleAttributes.Any(x => x.AttributeId == attribute.Id)) + { + ModalService.Info(new ConfirmOptions() { Title = "提示", Content = "该属性已经添加过了" }); + return; + } + + saleAttributeModel.AttributeId = attribute.Id; + saleAttributeModel.Name = attribute.Name; + saleAttributeModel.ControlType = attribute.ControlType; + saleAttributeModel.WeightIsRequired = attribute.WeightIsRequired; + saleAttributeModel.IsRequired = attribute.IsRequired; + } + StateHasChanged(); + } + + /// + /// 点击添加属性值按钮 + /// + /// + void OnClickAddAttributeValue(ProductSaleAttributeModel attribute) + { + foreach (var item in model.ProductSaleAttributes) + { + Console.WriteLine(item.ToJson()); + Console.WriteLine(attribute.ToJson()); + if (item.AttributeId == attribute.AttributeId) + { + attribute.AddAttribute = true; + editAttribute = attribute; + CurrentProductAttribute = productAttributes.FirstOrDefault(x => x.Id == attribute.AttributeId); + } + else + { + item.AddAttribute = false; + } + } + StateHasChanged(); + } + + void OnClickEditAttributeValue(ProductSaleAttributeModel attribute, ProductAttributeValueModel value) + { + value.IsEdit = true; + attribute.AddAttribute = false; + CurrentProductAttribute = productAttributes.FirstOrDefault(x => x.Id == attribute.AttributeId); + editAttributeValue = value; + Console.WriteLine($"编辑属性值:{value.ToJson()}"); + Console.WriteLine($"编辑属性值:{editAttributeValue.ToJson()}"); + } + + /// + /// 保存新的属性值 + /// + /// + /// + void OnClickEditAttributeValueForSave(ProductSaleAttributeModel attribute, ProductAttributeValueModel value) + { + + if (CurrentProductAttribute.ControlType == 0) + { + if (string.IsNullOrEmpty(value.Name)) + { + MessageService.Error("请输入规格值"); + return; + } + } + if (CurrentProductAttribute.WeightIsRequired) + { + if (value.Weight <= 0) + { + MessageService.Error("请输入重量"); + return; + } + } + var attributeValue = attribute.ProductAttributeValues.Where(p => p.Name == value.Name && p.Weight == value.Weight && p.DisplayOrder != value.DisplayOrder).SingleOrDefault(); + if (attributeValue != null) + { + MessageService.Error("已经设置了一个相同的属性值"); + return; + } + Console.WriteLine($"编辑属性值之后:{value.ToJson()}"); + value.IsEdit = false; + RebuildSkuCombinations(); + } + + void OnClickEditAttributeValueForCancel(ProductSaleAttributeModel attribute, ProductAttributeValueModel value) + { + value.IsEdit = false; + attribute.AddAttribute = false; + CurrentProductAttribute = new(); + } + + /// + /// 保存新的属性值 + /// + /// + /// + void HandleSaveAttributeValueAsync(long id) + { + var data = model.ProductSaleAttributes.SingleOrDefault(p => p.AttributeId == id); + if (data != null) + { + if (CurrentProductAttribute.ControlType == 0) + { + if (string.IsNullOrEmpty(editAttributeValue.Name)) + { + MessageService.Error("请输入规格值"); + return; + } + } + if (CurrentProductAttribute.WeightIsRequired) + { + if (editAttributeValue.Weight <= 0) + { + MessageService.Error("请输入重量"); + return; + } + } + var attributeValue = data.ProductAttributeValues.Where(p => p.Name == editAttributeValue.Name && p.Weight == editAttributeValue.Weight).SingleOrDefault(); + if (attributeValue != null) + { + MessageService.Error("已经设置了一个相同的属性值"); + return; + } + data.ProductAttributeValues.Add(new ProductAttributeValueModel() { Id = editAttributeValue.Id, Name = editAttributeValue.Name, Weight = editAttributeValue.Weight, WeightUnit = editAttributeValue.WeightUnit, DisplayOrder = data.ProductAttributeValues.Count }); + editAttributeValue = new(); + RebuildSkuCombinations(); + } + } + + void HandleCancelAttribute(long id) + { + var data = model.ProductSaleAttributes.SingleOrDefault(p => p.AttributeId == id); + if (data != null) + { + data.AddAttribute = false; + } + } + + void HandleRemoveAttributeValue(long attributeId, ProductAttributeValueModel value) + { + var data = model.ProductSaleAttributes.SingleOrDefault(p => p.AttributeId == attributeId); + data.ProductAttributeValues.Remove(value); + if (data.ProductAttributeValues.Count == 0) + { + + var attribute = model.PhotoSalesAttribute.SingleOrDefault(p => p.Id == data.AttributeId); + if (attribute != null) + { + model.PhotoSalesAttribute.Remove(attribute); + } + } + RebuildSkuCombinations(); + UpdateSkuPrices(); + } + + bool DisableMoveUpState(ProductSaleAttributeModel saleAttributeModel) + { + var index = saleAttributeModel.DisplayOrder; + if (index > 1) + { + if (model.ProductSaleAttributes.Count > 1) + { + return false; + } + } + return true; + } + + bool DisableMoveDownState(ProductSaleAttributeModel saleAttributeModel) + { + var index = saleAttributeModel.DisplayOrder; + if (index < 3 && index != model.ProductSaleAttributes.Count) + { + if (model.ProductSaleAttributes.Count > 1) + { + return false; + } + } + return true; + } + + void OnClickAttributeDisplayMoveUp(ProductSaleAttributeModel saleAttributeModel) + { + var index = saleAttributeModel.DisplayOrder; + if (index != 1) + { + var prev = model.ProductSaleAttributes.SingleOrDefault(p => p.DisplayOrder == index - 1); + if (prev == null) + { + return; + } + prev.DisplayOrder = prev.DisplayOrder + 1; + saleAttributeModel.DisplayOrder = saleAttributeModel.DisplayOrder - 1; + model.ProductSaleAttributes = model.ProductSaleAttributes.OrderBy(p => p.DisplayOrder).ToList(); + StateHasChanged(); + } + RebuildSkuCombinations(); + } + + void OnClickAttributeDisplayMoveDown(ProductSaleAttributeModel saleAttributeModel) + { + var index = saleAttributeModel.DisplayOrder; + if (index < 3 && index != model.ProductSaleAttributes.Count) + { + var next = model.ProductSaleAttributes.SingleOrDefault(p => p.DisplayOrder == index + 1); + if (next == null) + { + return; + } + next.DisplayOrder--; + saleAttributeModel.DisplayOrder++; + model.ProductSaleAttributes = model.ProductSaleAttributes.OrderBy(p => p.DisplayOrder).ToList(); + StateHasChanged(); + } + RebuildSkuCombinations(); + } + + void OnClickDeleteSaleAttribute(ProductSaleAttributeModel saleAttributeModel) + { + model.ProductSaleAttributes.Remove(saleAttributeModel); + var index = 1; + foreach (var item in model.ProductSaleAttributes) + { + item.DisplayOrder = index; + index++; + } + RebuildSkuCombinations(); + } + + void UploadFiles(IReadOnlyList files) + { + + } + + #region 分类相关 + + void BindCategory() + { + Category = Categories.SingleOrDefault(p => p.Id == model.CategoryId); + var path = Category?.Path.Split(",") ?? []; + if (path.Length > 0) + { + for (var i = 0; i < path.Length; i++) + { + if (i == 0) + { + level1CategoryId = path[i].ToLong(); + } + else if (i == 1) + { + level2CategoryId = path[i].ToLong(); + } + else if (i == 2) + { + level3CategoryId = path[i].ToLong(); + } + else + { + level4CategoryId = path[i].ToLong(); + } + } + } + Level1Categories = Categories.Where(p => p.ParentId == 0).ToList(); + + if (level1CategoryId > 0) + { + Level2Categories = Categories.Where(p => p.ParentId == level1CategoryId).ToList(); + } + if (level2CategoryId > 0) + { + Level3Categories = Categories.Where(p => p.ParentId == level2CategoryId).ToList(); + } + if (level3CategoryId > 0) + { + Level4Categories = Categories.Where(p => p.ParentId == level3CategoryId).ToList(); + } + + if (Category != null) + { + BuildCategoryPath(Category); + } + } + + async Task LoadCategories() + { + var url = $"/api/category/items"; + var apiResult = await HttpService.Get>>(url); + if (apiResult.Success) + { + Categories = apiResult.Data ?? new(); + } + } + + private void Level1CategoryValueChanged(long id) + { + level1CategoryId = id; + if (level1CategoryId > 0) + { + level2CategoryId = 0; + Level2Categories = Categories.Where(p => p.ParentId == id).ToList(); + } + else + { + Level2Categories = new(); + } + if (Level2Categories.Any()) + { + model.CategoryId = 0; + showLevel2Category = true; + showLevel3Category = false; + showLevel4Category = false; + level3CategoryId = 0; + level4CategoryId = 0; + Level3Categories = new(); + Level4Categories = new(); + } + else + { + model.CategoryId = level1CategoryId; + showLevel2Category = false; + showLevel3Category = false; + showLevel4Category = false; + level2CategoryId = 0; + level3CategoryId = 0; + level4CategoryId = 0; + Level3Categories = new(); + Level4Categories = new(); + } + } + + private void Level2CategoryValueChanged(long id) + { + level2CategoryId = id; + Level3Categories = Categories.Where(p => p.ParentId == id).ToList(); + if (Level3Categories.Any()) + { + model.CategoryId = 0; + showLevel3Category = true; + showLevel4Category = false; + level4CategoryId = 0; + Level4Categories = new(); + } + else + { + model.CategoryId = level2CategoryId; + showLevel3Category = false; + showLevel4Category = false; + level3CategoryId = 0; + level4CategoryId = 0; + Level4Categories = new(); + } + + } + + private void Level3CategoryValueChanged(long id) + { + level3CategoryId = id; + Level4Categories = Categories.Where(p => p.ParentId == id).ToList(); + if (Level4Categories.Any()) + { + model.CategoryId = 0; + showLevel4Category = true; + level4CategoryId = 0; + } + else + { + model.CategoryId = level3CategoryId; + showLevel4Category = false; + level4CategoryId = 0; + } + + } + + private void Level4CategoryValueChanged(long id) + { + level4CategoryId = id; + model.CategoryId = level4CategoryId; + } + + async Task OnClickNext() + { + if (model.CategoryId == 0) + { + ModalService.Info(new ConfirmOptions() { Title = "操作提示", Content = "请确认产品归属分类" }); + return; + } + var category = Categories.Where(p => p.Id == model.CategoryId).SingleOrDefault(); + if (category == null) + { + ModalService.Info(new ConfirmOptions() { Title = "操作提示", Content = "请设置正确的产品归属分类" }); + return; + } + BuildCategoryPath(category); + + editCategory = false; + await SearchAttribute(string.Empty); + } + #endregion + + void BuildCategoryPath(Category category) + { + Console.WriteLine(category.Name); + Console.WriteLine(category.Path); + var ids = category.Path.Split(","); + foreach (var id in ids) + { + var item = Categories.Where(p => p.Id == id.ToLong()).SingleOrDefault(); + if (item != null) + { + if (string.IsNullOrEmpty(CategoryPath)) + { + CategoryPath = item.Name; + } + else + { + CategoryPath = $"{CategoryPath} > {item.Name}"; + } + } + } + } + + int runNumber = 0; + + /// + /// 根据卡迪尔乘积构建UI表单 + /// + void RebuildSkuCombinations() + { + List> result = new List>(); + List TempSkuCombinations = new List(); + if (model.ProductSaleAttributes.Count > 0) + { + var list = model.ProductSaleAttributes.Where(p => p.ProductAttributeValues.Count > 0).ToList(); + Descartes(list, 0, result, new List()); + //int count = result.Count(); + + + foreach (var item in result) + { + + bool state = false; + foreach (var comb in model.ProductAttributeCombinations) + { + + if (comb.SkuAttributes.SequenceEqual(item)) + { + //新的SKU组合信息,原始SKU组合信息不能改动 + var combinationModel = new ProductAttributeCombinationModel() + { + Enabled = comb.Enabled, + Id = comb.Id, + MarketPrice = comb.MarketPrice, + Price = comb.Price, + ProductId = comb.ProductId, + SalesQuantity = comb.SalesQuantity, + SkuNumber = comb.SkuNumber, + SkuAttributes = item, + StockQuantity = comb.StockQuantity, + Weight = comb.Weight, + ManufacturerId = comb.ManufacturerId, + ProcessCharge = comb.ProcessCharge, + ProcessCost = comb.ProcessCost, + Surcharge = comb.Surcharge, + SurchargeCost = comb.SurchargeCost + }; + + TempSkuCombinations.Add(combinationModel); + state = true; + break; + } + } + + if (!state) + { + var combinationModel = new ProductAttributeCombinationModel() + { + Enabled = false, + Id = 0, + MarketPrice = 0, + Price = 0, + ProductId = model.Id, + SalesQuantity = 0, + SkuNumber = "", + SkuAttributes = item, + StockQuantity = 0, + Weight = 0, + }; + foreach (var sku in item) + { + if (sku.Weight > 0) + { + combinationModel.Weight = sku.Weight; + } + } + TempSkuCombinations.Add(combinationModel); + } + } + } + ProductAttributeCombinationsForEdit = TempSkuCombinations; + + Console.WriteLine($"执行步骤:{runNumber},RebuildSkuCombinations,数据:{model.ProductSaleAttributes.ToJson()}"); + runNumber++; + } + + /// + /// 笛卡尔乘积处理 + /// + /// + /// + /// + /// + /// + List Descartes(List list, int count, List> result, List data) + { + List tempData = data.Clone(); + if (list.Count() > 0) + { + ProductSaleAttributeModel saleAttribute = list[count]; + List attributeValues = saleAttribute.ProductAttributeValues; + foreach (var item in attributeValues) + { + if (count + 1 < list.Count()) + { + var copy2 = data.Clone(); + copy2.Add(new ProductSkuModel { AttributeId = saleAttribute.AttributeId, AttributeName = saleAttribute.Name, ValueId = item.Id, ValueName = item.Name, Weight = item.Weight.RemoveTrailingZeros().ToDecimal() }); + + var productSKu = Descartes(list, count + 1, result, copy2); + tempData.AddRange(productSKu); + } + else + { + var copy = data.Clone(); + var sku = new ProductSkuModel { AttributeId = saleAttribute.AttributeId, AttributeName = saleAttribute.Name, ValueId = item.Id, ValueName = item.Name, Weight = item.Weight.RemoveTrailingZeros().ToDecimal() }; + copy.Add(sku); + + result.Add(copy); + } + } + } + return tempData; + } + + string PriceFormat(decimal value) + { + return value.ToString("F2"); + } + + /// + /// 根据SKU规格变化更新产品信息中的价格 + /// + void UpdateSkuPrices() + { + if (ProductAttributeCombinationsForEdit.Any()) + { + model.MarketPrice = ProductAttributeCombinationsForEdit.Max(p => p.MarketPrice); + model.Price = ProductAttributeCombinationsForEdit.Max(p => p.Price); + model.StockQuantity = ProductAttributeCombinationsForEdit.Sum(p => p.StockQuantity); + } + else + { + model.MarketPrice = 0; + model.Price = 0; + model.StockQuantity = 0; + } + } + + /// + /// SKU市场价变化后更新商品市场价 + /// + void UpdateMarketPrice(ProductAttributeCombinationModel data, decimal price) + { + var marketPrice = price; + foreach (var item in ProductAttributeCombinationsForEdit) + { + if (item.SkuAttributes.SequenceEqual(data.SkuAttributes)) + { + item.MarketPrice = price; + } + if (item.MarketPrice > marketPrice) + { + marketPrice = item.Price; + } + } + model.MarketPrice = marketPrice; + + } + + /// + /// SKU销售价格变化后更新商品销售价格 + /// + void UpdatePrice(ProductAttributeCombinationModel data, decimal price) + { + var salePrice = price; + + foreach (var item in ProductAttributeCombinationsForEdit) + { + if (item.SkuAttributes.SequenceEqual(data.SkuAttributes)) + { + item.Price = price; + } + if (item.Price > salePrice) + { + salePrice = item.Price; + } + } + model.Price = salePrice; + } + + /// + /// SKU库存变化后更新库存 + /// + void UpdateStockQuantity(ProductAttributeCombinationModel data, int num) + { + int quantity = 0; + foreach (var item in ProductAttributeCombinationsForEdit) + { + if (item.SkuAttributes.SequenceEqual(data.SkuAttributes)) + { + item.StockQuantity = num; + } + quantity = quantity + item.StockQuantity; + } + model.StockQuantity = quantity; + StateHasChanged(); + } + + string GetWeightAndUnit(ProductSkuModel skuModel) + { + + switch (skuModel.WeightUnit) + { + case 1: + return $"{skuModel.Weight}mg"; + case 3: + return $"{skuModel.Weight}kg"; + case 4: + return $"{skuModel.Weight}t"; + case 5: + return $"{skuModel.Weight}lb"; + case 6: + return $"{skuModel.Weight}oz"; + default: + return $"{skuModel.Weight}g"; + } + } + + string GetWeightUnit(int weightUnit) + { + + switch (weightUnit) + { + case 1: + return $"mg"; + case 3: + return $"kg"; + case 4: + return $"t"; + case 5: + return $"lb"; + case 6: + return $"oz"; + default: + return $"g"; + } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductList.razor new file mode 100644 index 0000000..22747e5 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductList.razor @@ -0,0 +1,425 @@ +@page "/product/list" +@inject ILogger Logger +@attribute [Authorize] + +产品管理 + +产品列表 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + 产品列表 + + 新增 + + + + + + @context.Title + @context.Id + + + + + @if (context.Status == 1) + { + + + } + else + { + + } + + + + + + OnClickAddStock(context)">入库 + + + OnEditClick(context.Id)">编辑 + + + + 删除 + + + + + + + + + + + + + + + + + @stockModel.Title + + @if (!stockModel.AddAttribute) + { + @foreach (var item in stockModel.ProductStockAttributes) + { + + + + @foreach (var value in item.ProductAttributeValues) + { + + } + + + + + } + + } + + + 设置新规格 + + + @if (stockModel.AddAttribute) + { + @foreach (var item in stockModel.ProductStockAttributes) + { + + + @if (item.WeightIsRequired) + { + + @GetWeightUnit(item.WeightUnit) + + } + + } + } + + + + + + + + + + + + + + + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + ProductSearch search { get; set; } = new(); + Form searchForm = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + + [SupplyParameterFromForm] + ProductAddStockModel stockModel { get; set; } = new(); + Form stockForm = null!; + bool drawerVisible; + string WarehouseName = string.Empty; + List warehouseSelectItems = new(); + + public TypographyCopyableConfig CopyHint { get; set; } = null!; + + protected override void OnInitialized() + { + CopyHint = new() { OnCopy = () => MessageService.Success("复制成功") }; + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Title = query.GetQueryString("Title"); + search.CategoryId = query.GetQueryString("CategoryId"); + + var data = query.GetQueryString("RangeTime"); + if (!string.IsNullOrEmpty(data)) + { + var rangetime = data.Split("-"); + if (rangetime != null && rangetime.Length > 0) + { + searchExpand = true; + search.RangeTime[0] = rangetime[0].NumberToDateTime(); + search.RangeTime[1] = rangetime[1].NumberToDateTime(); + StateHasChanged(); + } + } + + } + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + MessageService.Info("正在查询数据..."); + OnSearch(Page.Value); + } + + + private void OnSearchReset() + { + search = new(); + LoadList(); + } + + private async void LoadList() + { + + loading = true; + var url = "/api/product/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + + private void OnReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/product/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/product/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/product/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/product/list?{queryString}"); + } + } + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/product/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + + + void OnPageChanged(PaginationEventArgs args) + { + OnSearch(args.Page); + } + + + + + void OnCreateClick() + { + Navigation.NavigateTo($"/product/create"); + } + + void OnEditClick(long id) + { + Navigation.NavigateTo($"/product/edit/{id}"); + } + + async Task OnClickAddStock(Product product) + { + drawerVisible = true; + stockModel = product.Adapt(); + stockModel.ProductId = product.Id; + + var url = $"/api/product/attributes/{product.Id}"; + var apiResult = await HttpService.Post>>(url, new()); + if (apiResult.Success) + { + stockModel.ProductStockAttributes = apiResult.Data ?? new(); + } + + WarehouseSearch warehouseSearch = new() + { + CorporationId = product.CorporationId + }; + url = $"/api/warehouse/select"; + var selectResult = await HttpService.Post>>(url, warehouseSearch); + if (selectResult.Success) + { + warehouseSelectItems = selectResult.Data ?? new(); + } + } + + async void OnWarehouseInput(ChangeEventArgs e) + { + WarehouseSearch warehouseSearch = new() + { + CorporationId = stockModel.CorporationId, + Name = e.Value?.ToString() + }; + var url = $"/api/warehouse/select"; + var selectResult = await HttpService.Post>>(url, warehouseSearch); + if (selectResult.Success) + { + warehouseSelectItems = selectResult.Data ?? new(); + } + } + + Func CompareWith = (a, b) => + { + if (a is SelectItem o1 && b is SelectItem o2) + { + return o1.Name == o2.Name; + } + else + { + return false; + } + }; + + void CloseDrawer() + { + drawerVisible = false; + stockForm.Reset(); + } + + async Task OnStockFormFinish() + { + if (stockForm.Validate()) + { + var result = new ApiResult(); + + Console.WriteLine(stockModel.ToJson()); + + var url = $"api/product/stock/add"; + result = await HttpService.Post>(url, stockModel); + + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + string GetAttributeValue(ProductAttributeValueModel value) + { + + if (value.Weight > 0) + { + return $"{value.Name}({value.Weight.RemoveTrailingZeros()}{GetWeightUnit(value.WeightUnit)})"; + } + else + { + return value.Name; + } + } + + string GetWeightUnit(int weightUnit) + { + + switch (weightUnit) + { + case 1: + return $"mg"; + case 3: + return $"kg"; + case 4: + return $"t"; + case 5: + return $"lb"; + case 6: + return $"oz"; + default: + return $"g"; + } + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductStockList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductStockList.razor new file mode 100644 index 0000000..3df4f45 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/ProductStockList.razor @@ -0,0 +1,258 @@ +@page "/product/stock/list" + +@inject ILogger Logger +@attribute [Authorize] + +产品库存列表 + + + + + 产品库存列表 + + + + + + @foreach (var item in context.Sku) + { + + @item.ValueName + @if (item.Weight > 0) + { + (@($"{item.Weight}{@GetWeightUnit(item.WeightUnit)}")) + } + + } + + + @context.Weight.RemoveTrailingZeros() + + + + + + + + OnClickAddStock(context)">入库 + + + + + + + + + + + + + + + + + @model.Title + + + @if (model.Sku != null) + { + @foreach (var item in model.Sku) + { + + @item.AttributeName + + @item.ValueName + @if (item.Weight > 0) + { + (@($"{item.Weight}{@GetWeightUnit(item.WeightUnit)}")) + } + + + } + } + + + + + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + ProductStockUnitSearch search { get; set; } = new(); + Form searchForm = null!; + + PagingList PagingList = new(); + + [SupplyParameterFromForm] + ProductStockModel model { get; set; } = new(); + Form editform = null!; + + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + private bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Key = query.GetQueryString("Key"); + search.Type = query.GetQueryString("Type").ToInt(); + } + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + + private void OnSearchReset() + { + search = new(); + LoadList(); + } + + private async void LoadList() + { + + loading = true; + var url = "/api/product/stock/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + + private void OnReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/product/stock/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/product/stock/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/product/stock/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/product/stock/list?{queryString}"); + } + } + } + + + + void OnPageChanged(PaginationEventArgs args) + { + OnSearch(args.Page); + } + + string PriceFormat(decimal value) + { + return value.ToString("G"); + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } + + void OnClickAddStock(ProductStockUnitModel item) + { + model = item.Adapt(); + drawerVisible = true; + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + var result = new ApiResult(); + + + + var url = $"api/product/skustock/add"; + result = await HttpService.Post>(url, model); + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + string GetWeightUnit(int weightUnit) + { + + switch (weightUnit) + { + case 1: + return $"mg"; + case 3: + return $"kg"; + case 4: + return $"t"; + case 5: + return $"lb"; + case 6: + return $"oz"; + default: + return $"g"; + } + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Products/SpecificationList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/SpecificationList.razor new file mode 100644 index 0000000..0c9600d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Products/SpecificationList.razor @@ -0,0 +1,319 @@ +@page "/specification/list" + +@inject ILogger Logger +@attribute [Authorize] + + +产品规格管理 + +属性列表 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + 属性列表 + + 新增 + + + + + + + @if (context.ControlType == 1) + { + 选项 + } + else + { + 文本框 + } + + + + + @if (context.Status == 1) + { + + + } + else + { + + } + + + + + + OnViewPredefinedListClick(context.Id)">预设值 + + + OnEditClick(context)">编辑 + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + 默认(文本) + 选项 + + + + + 启用 + 禁用 + + + + 保存 + + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery] + long CategoryId { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + SpecificationAttributeSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + SpecificationAttributeModel model { get; set; } = new(); + Form editform = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + private bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Name = query.GetQueryString("Name"); + search.CategoryId = CategoryId; + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/specificationattribute/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + + private void OnSearchReset() + { + search = new(); + LoadList(); + } + + private void OnReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/specification/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/specification/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/specification/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/specification/list?{queryString}"); + } + } + } + + + void OnEditClick(SpecificationAttribute attribute) + { + this.model = attribute.Adapt(); + drawerVisible = true; + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + + var url = $"/api/specificationattribute/delete/{model.Id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + if (CategoryId > 0) + { + model.CategoryId = CategoryId; + } + + if (editform.Validate()) + { + var result = new ApiResult(); + + + if (model.Id > 0) + { + var url = $"api/specificationattribute/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/specificationattribute/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + + void OnPageChanged(PaginationEventArgs args) + { + + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + Navigation.NavigateTo($"/specification/list?page={args.Page}"); + } + else + { + Navigation.NavigateTo($"/specification/list?page={Page}&{queryString}"); + } + } + + + void OnCreateClick() + { + drawerVisible = true; + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } + + void OnViewPredefinedListClick(long id) + { + Navigation.NavigateTo($"/specification/Predefined/list/{id}"); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Register.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Register.razor new file mode 100644 index 0000000..0081d77 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Register.razor @@ -0,0 +1,10 @@ +@page "/account/register" +@inject ILogger Logger + +注册账号 + +Register + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Settings/MessageTemplateList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Settings/MessageTemplateList.razor new file mode 100644 index 0000000..c4d8936 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Settings/MessageTemplateList.razor @@ -0,0 +1,317 @@ +@page "/setting/messagetemplate/list" + +@inject ILogger Logger +@* @attribute [Authorize] *@ + +消息模板 +消息模版管理 + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + 菜单列表 + + 新增 + + + + + + @if (context.Type == (int)MessageTemplateType.Message) + { + 站内信 + } + else if (context.Type == (int)MessageTemplateType.Email) + { + 邮件 + } + else if (context.Type == (int)MessageTemplateType.Sms) + { + 短信 + } + + + + + + @if (context.Enabled) + { + + + } + else + { + + } + + + + + + + + + + + HandleEdit(context)"> 编辑 + + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 启用 + + + + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromForm] + MessageTemplateSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + MessageTemplateModel template { get; set; } = new(); + Form editForm = null!; + + + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + bool modalVisible { get; set; } = false; + bool modalLoading { get; set; } = false; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Key = query.GetQueryString("Key"); + } + + private async void LoadList() + { + + loading = true; + var url = "/api/messagetemplate/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1) - 1); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + void OnSearchReset() + { + search = new(); + searchForm?.Reset(); + OnSearchFinish(); + } + + void OnPageChanged(PaginationEventArgs args) + { + OnSearch(args.Page); + } + + private void OnReset() + { + search = new(); + LoadList(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/setting/messagetemplate/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/setting/messagetemplate/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/setting/messagetemplate/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/setting/messagetemplate/list?{queryString}"); + } + } + LoadList(); + } + + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"api/messagetemplate/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + void OnCreateClick() + { + template = new(); + modalVisible = true; + } + + void HandleEdit(MessageTemplate model) + { + template = model.Adapt(); + modalVisible = true; + } + + void HandleModalOk() + { + editForm.Submit(); + } + + async Task OnFormFinish() + { + if (editForm.Validate()) + { + var result = new ApiResult(); + var data = template.Adapt(); + + if (template.Id > 0) + { + var url = $"api/messagetemplate/edit"; + result = await HttpService.Post>(url, data); + } + else + { + var url = $"api/messagetemplate/add"; + result = await HttpService.Post>(url, data); + } + + if (result.Code == (int)ResultCode.Success) + { + modalVisible = false; + LoadList(); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + void HandleCancel() + { + modalVisible = false; + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Settings/Settings.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Settings/Settings.razor new file mode 100644 index 0000000..edb1f15 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Settings/Settings.razor @@ -0,0 +1,7 @@ +@page "/settings" +@* @attribute [Authorize] *@ +@inject ILogger Logger + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/SiteApps/AppVersionList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/SiteApps/AppVersionList.razor new file mode 100644 index 0000000..eef040d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/SiteApps/AppVersionList.razor @@ -0,0 +1,10 @@ +@page "/system/app/version/list" + +@inject ILogger Logger +@inject HttpService HttpService +@* @attribute [Authorize] *@ + + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/SiteApps/SiteAppList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/SiteApps/SiteAppList.razor new file mode 100644 index 0000000..4e38064 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/SiteApps/SiteAppList.razor @@ -0,0 +1,7 @@ +@page "/system/app/list" +@inject ILogger Logger +@* @attribute [Authorize] *@ + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Storages/UploadList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Storages/UploadList.razor new file mode 100644 index 0000000..674f0b7 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Storages/UploadList.razor @@ -0,0 +1,291 @@ +@page "/system/file/list" + +上传文件 +上传文件 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + 文件列表 + + 上传新文件 + + + + + + + + + + + + @if (context.Type == 1) + { + + + } + else + { + + } + + + + + + + + + + + + OnEditClick(context)"> 编辑 + + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + UploadFileSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + UploadFileModel model { get; set; } = new(); + Form editform = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + + bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Name = query.GetQueryString("Name"); + + var data = query.GetQueryString("RangeTime"); + if (!string.IsNullOrEmpty(data)) + { + var rangetime = data.Split("-"); + if (rangetime != null && rangetime.Length > 0) + { + searchExpand = true; + search.RangeTime[0] = rangetime[0].NumberToDateTime(); + search.RangeTime[1] = rangetime[1].NumberToDateTime(); + StateHasChanged(); + } + } + + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/uploadfile/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + + private void OnReset() + { + search = new(); + LoadList(); + } + + void OnSearchReset() + { + search = new(); + searchForm?.Reset(); + OnSearchFinish(); + } + + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/system/upload/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/system/upload/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/system/upload/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/system/upload/list?{queryString}"); + } + } + } + + void OnCreateClick() + { + model = new(); + drawerVisible = true; + } + + void OnEditClick(UploadFile file) + { + this.model = file.Adapt(); + drawerVisible = true; + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/uploadfile/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + + var result = new ApiResult(); + if (model.Id > 0) + { + var url = $"api/uploadfile/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/uploadfile/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + private void OnPageChanged(int args) + { + OnSearch(args); + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/AdminList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/AdminList.razor new file mode 100644 index 0000000..92334fa --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/AdminList.razor @@ -0,0 +1,347 @@ +@page "/admin/list" +@inject ILogger Logger +@* @attribute [Authorize] *@ + + +管理员账号管理 + +管理员帐号 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + 帐号列表 + + + + 新增 + + + 没有权限 + + + + + + + + + + + + + + @if (context.Status == 1) + { + + + } + else + { + + } + + + + + + + + + + + + OnEditClick(context)"> 编辑 + + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if (context.Id > 0) + { + + + 重置密码 + + + } + @if (context.Id == 0) + { + + + + } + @if ((context.Id > 0 && context.SetPassword) || context.Id == 0) + { + + + + + } + @if ((context.Id > 0 && context.SetPassword) || context.Id == 0) + { + + + + + } + + + 禁用 + 启用 + + + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + AdminSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + AdminModel model { get; set; } = new(); + Form editform = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + bool searchExpand { get; set; } = false; + + bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Username = query.GetQueryString("Username"); + search.Mobile = query.GetQueryString("Mobile"); + search.Email = query.GetQueryString("Email"); + search.RealName = query.GetQueryString("RealName"); + search.Status = query.GetQueryString("Status"); + + var data = query.GetQueryString("RangeTime"); + if (!string.IsNullOrEmpty(data)) + { + var rangetime = data.Split("-"); + if (rangetime != null && rangetime.Length > 0) + { + searchExpand = true; + search.RangeTime[0] = rangetime[0].NumberToDateTime(); + search.RangeTime[1] = rangetime[1].NumberToDateTime(); + StateHasChanged(); + } + } + + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/admin/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + + private void OnReset() + { + search = new(); + LoadList(); + } + + void OnSearchReset() + { + search = new(); + searchForm?.Reset(); + OnSearchFinish(); + } + + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/admin/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/admin/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/admin/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/admin/list?{queryString}"); + } + } + } + + void OnCreateClick() + { + model = new(); + drawerVisible = true; + } + + void OnEditClick(Admin admin) + { + this.model = admin.Adapt(); + this.model.Password = string.Empty; + drawerVisible = true; + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/admin/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + + var result = new ApiResult(); + if (model.Id > 0) + { + var url = $"api/admin/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/admin/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + private void OnPageChanged(int args) + { + OnSearch(args); + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/CurrencyList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/CurrencyList.razor new file mode 100644 index 0000000..7922a44 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/CurrencyList.razor @@ -0,0 +1,7 @@ +@page "/system/currency/list" + +CurrencyList + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/LanguageList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/LanguageList.razor new file mode 100644 index 0000000..5c2f85d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/LanguageList.razor @@ -0,0 +1,238 @@ +@page "/system/language/list" +@inject ILogger Logger + +语言管理 + +多语言 + + + + + + 多语言列表 + + 新增 + + + + + + + + + + + + @if (context.Enabled) + { + + + } + else + { + + } + + + + + + + + + + + + OnEditClick(context)"> 编辑 + + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @* + + *@ + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + LanguageSearch search { get; set; } = new(); + + [SupplyParameterFromForm] + LanguageModel model { get; set; } = new(); + Form editform = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + + bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + LoadList(); + + base.OnParametersSet(); + } + + + private async void LoadList() + { + + loading = true; + var url = "/api/language/search"; + var apiResult = await HttpService.GetPagingList(url, search, Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/system/language/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/system/language/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/system/language/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/system/language/list?{queryString}"); + } + } + } + + void OnCreateClick() + { + model = new(); + drawerVisible = true; + } + + void OnEditClick(Language admin) + { + this.model = admin.Adapt(); + drawerVisible = true; + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/language/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + + var result = new ApiResult(); + if (model.Id > 0) + { + var url = $"api/language/edit"; + result = await HttpService.Post>(url, model); + } + else + { + var url = $"api/language/add"; + result = await HttpService.Post>(url, model); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + } + + private void OnPageChanged(int args) + { + OnSearch(args); + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/LocaleResourceList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/LocaleResourceList.razor new file mode 100644 index 0000000..7fe920d --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/LocaleResourceList.razor @@ -0,0 +1,6 @@ +@page "/system/locale/resource/list/{culture}" +@inject ILogger Logger + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/MenuList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/MenuList.razor new file mode 100644 index 0000000..c03a338 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/MenuList.razor @@ -0,0 +1,350 @@ +@page "/system/menu/list" + +@inject ILogger Logger +@using MenuItem = Atomx.Admin.Client.Models.MenuItem +@using Menu = Atomx.Common.Entities.Menu +@* @attribute [Authorize] *@ + +菜单管理 + +菜单管理 + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + 菜单列表 + + 新增 + + + + + + + @GetName(context) + + + @if (!string.IsNullOrEmpty(context.Icon)) { + + } + + + + + + @if (context.Enabled) + { + + + } + else + { + + } + + + + + + OnEditClick(context)">编辑 + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + @foreach(var item in Menus) + { + + } + + + + + @if (menu.ParentId == 0) + { + + + + + @Icon + + + @Icon + + + + } + + + + + + + + 保存 + + + + + +@code { + + + + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromForm] + MenuSearch search { get; set; } = new(); + Form searchForm = null!; + + [SupplyParameterFromForm] + MenuModel menu { get; set; } = new(); + Form editform = null!; + + List Menus = new(); + + private bool drawerVisible; + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + loadQueryString(); + LoadList(); + + base.OnParametersSet(); + } + + void loadQueryString() + { + var uri = new Uri(Navigation.Uri); + var query = uri.Query; + search.Name = query.GetQueryString("Name"); + } + + private async void LoadList() + { + var url = "/api/menu/list"; + var apiResult = await HttpService.Get>>(url); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + Menus = apiResult.Data; + StateHasChanged(); + } + } + } + + void OnSearchFinish() + { + Page = Page.GetValueOrDefault(1) - 1; + + OnSearch(Page.Value); + } + + void OnSearchReset() + { + search = new(); + searchForm?.Reset(); + OnSearchFinish(); + } + + private void OnReset() + { + search = new(); + LoadList(); + + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/system/menu/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/system/menu/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/system/menu/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/system/menu/list?{queryString}"); + } + } + LoadList(); + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/menu/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + LoadList(); + ModalService.Info(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + ModalService.Error(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + void OnCreateClick() + { + menu = new(); + drawerVisible = true; + } + + void OnEditClick(Common.Entities.Menu model) + { + menu = model.Adapt(); + drawerVisible = true; + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + var result = new ApiResult(); + var data = menu.Adapt(); + if (menu.Id > 0) + { + var url = $"api/menu/edit"; + result = await HttpService.Post>(url, data); + } + else + { + var url = $"api/menu/add"; + result = await HttpService.Post>(url, data); + } + + if (result.Code == (int)ResultCode.Success) + { + + CloseDrawer(); + LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title="提示", Content="数据提交成功!" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = result.Message }); + } + } + + + + } + + private string GetPath(string path) + { + var array = path.Split(",").ToList(); + var pathText = string.Empty; + foreach (var item in array) + { + var name = Menus.Where(p => p.Id == item.ToLong()).Select(p => p.Name).SingleOrDefault(); + if (string.IsNullOrEmpty(pathText)) + { + pathText = name; + } + else + { + pathText = $"{pathText}>{name}"; + } + } + return pathText ?? string.Empty; + } + + private string GetName(MenuItem menu) + { + string name = string.Empty; + if (menu.Depth > 0) + { + var symbol = " "; + for (var i = 0; i < menu.Depth; i++) + { + symbol = $"{symbol}├ "; + } + name = $"{symbol}{menu.Name}"; + } + else + { + name = menu.Name; + } + return name; + } + + + + private void OpenDrawer() + { + drawerVisible = true; + } + + private void CloseDrawer() + { + menu = new(); + drawerVisible = false; + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/PermissionEdit.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/PermissionEdit.razor new file mode 100644 index 0000000..6a62b8b --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/PermissionEdit.razor @@ -0,0 +1,186 @@ +@page "/system/role/permission/{RoleId:long}" +@inject ILogger Logger + +权限设置 + + 编辑角色权限 + + + + 为角色 @role?.Name 设置权限 + + + + @if (!PermissionGroups.Any()) + { + + 暂无权限可设置 + + } + else + { + @foreach (var group in PermissionGroups) + { + + + + + ToggleAllPermissions(group, (bool)e.Value!)" + checked="@group.PermissionItems.All(p => p.IsSelected)" /> + + + + + + @group.CategoryName + + + (@group.PermissionItems.Count(p => p.IsSelected)/@group.PermissionItems.Count) + + + + + + + @foreach (var permission in group.PermissionItems) + { + + + + + + + + + @permission.Description + @permission.Name + + + + } + + } + } + + + + + @if (isSaving) + { + 保存中... + } + else + { + 保存权限设置 + } + + + + + + + +@code { + [Parameter] + public long RoleId { get; set; } + + bool loading = false; + bool isSaving = false; + Role role; + List PermissionGroups = new List(); + + [SupplyParameterFromForm] + RoleModel model { get; set; } = new(); + Form editForm = null!; + + + protected override void OnInitialized() + { + base.OnInitialized(); + } + + protected override async Task OnParametersSetAsync() + { + loading = true; + + await LoadRoleAndPermission(); + + loading = false; + base.OnParametersSet(); + } + + async Task LoadRoleAndPermission() + { + var roleResult = await HttpService.Get>($"/api/role/{RoleId}"); + if (roleResult.Success) + { + if (roleResult.Data != null) + { + role = roleResult.Data; + StateHasChanged(); + } + } + + + var apiResult = await HttpService.Get>>($"/api/role/permission/{RoleId}"); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PermissionGroups = apiResult.Data; + StateHasChanged(); + } + } + } + + private void ToggleAllPermissions(RolePermissionGroup group, bool isSelected) + { + foreach (var permission in group.PermissionItems) + { + permission.IsSelected = isSelected; + } + StateHasChanged(); + } + + async void OnFormFinishAsync() + { + isSaving = true; + StateHasChanged(); + + try + { + var selectedPermissions = PermissionGroups! + .SelectMany(g => g.PermissionItems) + .Where(p => p.IsSelected) + .Select(p => p.Name) + .ToList(); + + model = role.Adapt(); + model.Permission = string.Join(",", selectedPermissions); + + var url = $"api/role/edit"; + var result = await HttpService.Post>(url, model); + if (result.Success) + { + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "权限设置保存成功!" }); + } + else + { + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "服务异常!" }); + } + Navigation.NavigateTo("/system/role/list"); + } + catch (Exception ex) + { + await ModalService.InfoAsync(new ConfirmOptions() { Title = "提示", Content = "保存权限设置失败!" }); + } + finally + { + isSaving = false; + StateHasChanged(); + } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/RoleList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/RoleList.razor new file mode 100644 index 0000000..707b8ab --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Systems/RoleList.razor @@ -0,0 +1,259 @@ +@page "/system/role/list" +@inject ILogger Logger + +角色管理 + +角色管理 + + + + + + 角色列表 + + 新增 + + + + + + + + + @if (context.IsSystemRole) + { + + } + else + { + + } + + + + @if (context.Enabled) + { + + + } + else + { + + } + + + + + + + 权限管理 + + + + + + + + OnEditClick(context)"> 编辑 + + @if (!context.IsSystemRole) + { + + + + 删除 + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 保存 + + + + +@code { + [SupplyParameterFromQuery] + int? Page { get; set; } + + [SupplyParameterFromQuery(Name = "size")] + int? PageSize { get; set; } + + [SupplyParameterFromForm] + RoleSearch search { get; set; } = default!; + + [SupplyParameterFromForm] + RoleModel model { get; set; } = default!; + Form editform = null!; + + PagingList PagingList = new(); + bool loading { get; set; } = true; + + bool drawerVisible; + bool isSave = false; + + protected override void OnInitialized() + { + search ??= new RoleSearch(); + model ??= new RoleModel(); + base.OnInitialized(); + } + + protected override async Task OnParametersSetAsync() + { + await LoadList(); + } + + + private async Task LoadList() + { + + loading = true; + var url = "/api/role/search"; + var apiResult = await HttpService.GetPagingList(url, search ?? new RoleSearch(), Page.GetValueOrDefault(1), PageSize.GetValueOrDefault(20)); + if (apiResult.Success) + { + if (apiResult.Data != null) + { + PagingList = apiResult.Data; + } + } + loading = false; + StateHasChanged(); + } + + private void OnSearch(int page) + { + var queryString = search.BuildQueryString(); + if (string.IsNullOrEmpty(queryString)) + { + if (page > 1) + { + Navigation.NavigateTo($"/system/role/list?page={page}"); + } + else + { + Navigation.NavigateTo($"/system/role/list"); + } + } + else + { + if (page > 1) + { + Navigation.NavigateTo($"/system/role/list?page={page}&{queryString}"); + } + else + { + Navigation.NavigateTo($"/system/role/list?{queryString}"); + } + } + } + + void OnCreateClick() + { + model = new(); + drawerVisible = true; + } + + void OnEditPermissionClick(Role role) + { + Navigation.NavigateTo($"/system/role/permission/{role.Id}"); + } + + void OnEditClick(Role role) + { + model = role.Adapt(); + drawerVisible = true; + } + + async Task HandleDeleteConfirmAsync(MouseEventArgs e, long id) + { + var url = $"/api/role/delete/{id}"; + var apiResult = await HttpService.Post>(url, new()); + if (apiResult.Success) + { + await LoadList(); + await ModalService.InfoAsync(new ConfirmOptions() { Title = "操作提示", Content = "删除数据成功" }); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "操作提示", Content = $"数据删除失败.{apiResult.Message}" }); + } + } + + async Task OnFormFinish() + { + if (editform.Validate()) + { + var apiResult = new ApiResult(); + if (model != null) + { + if (model.Id > 0) + { + var url = $"api/role/edit"; + apiResult = await HttpService.Post>(url, model); + } + else + { + var url = $"api/role/add"; + apiResult = await HttpService.Post>(url, model); + } + if (apiResult.Success) + { + ModalService.Success(new ConfirmOptions() { Title = "提示", Content = "数据提交成功!" }); + CloseDrawer(); + await LoadList(); + } + else + { + await ModalService.ErrorAsync(new ConfirmOptions() { Title = "服务异常", Content = apiResult.Message }); + } + } + } + StateHasChanged(); + } + + private void OnPageChanged(PaginationEventArgs args) + { + OnSearch(args.Page); + } + + void CloseDrawer() + { + drawerVisible = false; + editform.Reset(); + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Users/UserList.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Users/UserList.razor new file mode 100644 index 0000000..3c972e0 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Users/UserList.razor @@ -0,0 +1,5 @@ +UserList + +@code { + +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Pages/Weather.razor b/Atomx.Admin/Atomx.Admin.Client/Pages/Weather.razor new file mode 100644 index 0000000..dd36b18 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Pages/Weather.razor @@ -0,0 +1,63 @@ +@page "/weather" + +Weather + +Weather + +This component demonstrates showing data. + +@if (forecasts == null) +{ + Loading... +} +else +{ + + + + Date + Temp. (C) + Temp. (F) + Summary + + + + @foreach (var forecast in forecasts) + { + + @forecast.Date.ToShortDateString() + @forecast.TemperatureC + @forecast.TemperatureF + @forecast.Summary + + } + + +} + +@code { + private WeatherForecast[]? forecasts; + + protected override async Task OnInitializedAsync() + { + // Simulate asynchronous loading to demonstrate a loading indicator + await Task.Delay(500); + + var startDate = DateOnly.FromDateTime(DateTime.Now); + var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; + forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = startDate.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }).ToArray(); + } + + private class WeatherForecast + { + public DateOnly Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Program.cs b/Atomx.Admin/Atomx.Admin.Client/Program.cs new file mode 100644 index 0000000..a22764a --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Program.cs @@ -0,0 +1,24 @@ +using Atomx.Admin.Client.Services; +using Atomx.Admin.Client.Utils; +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +builder.Services.AddBlazoredLocalStorageAsSingleton(); + +builder.Services.AddAuthorizationCore(); +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddHttpClientApiService(builder.Configuration["WebApi:ServerUrl"] ?? builder.HostEnvironment.BaseAddress); + +builder.Services.AddAntDesign(); + + +await builder.Build().RunAsync(); diff --git a/Atomx.Admin/Atomx.Admin.Client/RedirectToLogin.razor b/Atomx.Admin/Atomx.Admin.Client/RedirectToLogin.razor new file mode 100644 index 0000000..0ed1dcf --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/RedirectToLogin.razor @@ -0,0 +1,8 @@ +@inject NavigationManager NavigationManager + +@code { + protected override void OnInitialized() + { + Navigation.NavigateTo($"/account/login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}", forceLoad: true); + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Routes.razor b/Atomx.Admin/Atomx.Admin.Client/Routes.razor new file mode 100644 index 0000000..2771534 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Routes.razor @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Services/ClientPermissionService.cs b/Atomx.Admin/Atomx.Admin.Client/Services/ClientPermissionService.cs new file mode 100644 index 0000000..535bbd5 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Services/ClientPermissionService.cs @@ -0,0 +1,124 @@ +using Atomx.Common.Utils; +using Microsoft.AspNetCore.Components.Authorization; +using System.Net.Http.Json; +using System.Security.Claims; + +namespace Atomx.Admin.Client.Services +{ + public interface IPermissionService + { + /// + /// 是否拥有权限点 + /// + /// + /// + Task HasPermissionAsync(string permission); + + /// + /// 是否拥有指定权限中的一个 + /// + /// + /// + Task HasAnyPermissionAsync(params string[] permissions); + + /// + /// 是否拥有指定权限中的所有权限 + /// + /// + /// + Task HasAllPermissionsAsync(params string[] permissions); + + /// + /// 获取用户的所有权限 + /// + /// + Task> GetUserPermissionsAsync(); + //Task> GetUserRolesAsync(); + } + + public class ClientPermissionService : IPermissionService + { + private readonly AuthenticationStateProvider _authenticationStateProvider; + private readonly HttpClient _httpClient; + + public ClientPermissionService( + AuthenticationStateProvider authenticationStateProvider, + HttpClient httpClient) + { + _authenticationStateProvider = authenticationStateProvider; + _httpClient = httpClient; + } + + public async Task HasPermissionAsync(string permission) + { + // 客户端检查(基于声明) + var authState = await _authenticationStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + + if (!user.Identity?.IsAuthenticated ?? true) + return false; + + // 检查声明中的权限 + var hasPermission = user.Claims.Any(c => + c.Type == ClaimKeys.Permission && c.Value == permission); + + if (hasPermission) + return true; + + // 如果声明中没有,调用API验证 + //try + //{ + // return await _httpClient.GetFromJsonAsync($"/api/auth/haspermission?permission={permission}"); + //} + //catch + //{ + // return false; + //} + return false; + } + + public async Task HasAnyPermissionAsync(params string[] permissions) + { + foreach (var permission in permissions) + { + if (await HasPermissionAsync(permission)) + return true; + } + return false; + } + + public async Task HasAllPermissionsAsync(params string[] permissions) + { + foreach (var permission in permissions) + { + if (!await HasPermissionAsync(permission)) + return false; + } + return true; + } + + public async Task> GetUserPermissionsAsync() + { + try + { + return await _httpClient.GetFromJsonAsync>("/api/auth/permissions") + ?? new List(); + } + catch + { + return new List(); + } + } + + public async Task> GetUserRolesAsync() + { + var authState = await _authenticationStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + + return user.Claims + .Where(c => c.Type == ClaimTypes.Role) + .Select(c => c.Value) + .ToList(); + } + } +} \ No newline at end of file diff --git a/Atomx.Admin/Atomx.Admin.Client/Services/HttpService.cs b/Atomx.Admin/Atomx.Admin.Client/Services/HttpService.cs new file mode 100644 index 0000000..815ce68 --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Services/HttpService.cs @@ -0,0 +1,67 @@ +using Atomx.Common.Models; +using Atomx.Utils.Json; +using System.Net.Http.Json; +using System.Text; + +namespace Atomx.Admin.Client.Services +{ + public class HttpService(HttpClient httpClient) + { + public async Task Get(string url) + { + var response = await httpClient.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + return content.FromJson(); + } + else + { + throw new Exception($"Error: {response.StatusCode}"); + } + } + + public async Task Post(string url, object data) + { + var json = data.ToJson(); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await httpClient.PostAsync(url, content); + if (response.IsSuccessStatusCode) + { + var responseContent = await response.Content.ReadAsStringAsync(); + return responseContent.FromJson(); + } + else + { + throw new Exception($"Error: {response.StatusCode}"); + } + } + + public async Task>> GetPagingList(string url, object data, int page, int size = 20) + { + try + { + if (page < 1) + { + page = 1; + } + url = $"{url}?page={page}&size={size}"; + var response = await httpClient.PostAsJsonAsync(url, data); + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + return content.FromJson>>(); + } + else + { + throw new Exception($"Error: {response.StatusCode}"); + } + } + catch (HttpRequestException ex) + { + Console.WriteLine(ex.ToString()); + throw new Exception($"api {url} service failure"); + } + } + } +} diff --git a/Atomx.Admin/Atomx.Admin.Client/Services/LocalizationClientService.cs b/Atomx.Admin/Atomx.Admin.Client/Services/LocalizationClientService.cs new file mode 100644 index 0000000..144fcfd --- /dev/null +++ b/Atomx.Admin/Atomx.Admin.Client/Services/LocalizationClientService.cs @@ -0,0 +1,325 @@ +using Atomx.Common.Models; +using Microsoft.JSInterop; +using System.Text.Json; + +namespace Atomx.Admin.Client.Services +{ + public interface ILocalizationService + { + /// + /// 根据name获取制定文化语言的译文 + /// + /// + /// + /// + Task GetStringAsync(string name, string? culture = null); + + /// + /// 把本地化文化语言加载到内存中 + /// + /// + /// + Task LoadResourcesAsync(string culture); + + + event EventHandler? ResourcesUpdated; + } + + public class LocalizationClientService : ILocalizationService, IAsyncDisposable + { + private readonly HttpService _httpService; + private readonly HttpClient _httpClient; + private readonly IJSRuntime _jsRuntime; + private readonly ILogger _logger; + + private readonly Dictionary> _resources = new(); + private readonly Dictionary _versions = new(); + private readonly SemaphoreSlim _semaphore = new(1, 1); + + public event EventHandler? ResourcesUpdated; + + public LocalizationClientService( + HttpService httpService, + HttpClient httpClient, + IJSRuntime jsRuntime, + ILogger logger) + { + _httpService = httpService; + _httpClient = httpClient; + _jsRuntime = jsRuntime; + _logger = logger; + } + + /// + /// 根据name获取制定文化语言的译文 + /// + /// + /// + /// + public async Task GetStringAsync(string name, string? culture = null) + { + culture ??= await GetCurrentCultureAsync(); + + if (_resources.TryGetValue(culture, out var cultureResources)) + { + if (cultureResources.TryGetValue(name, out var value)) + { + return value; + } + + // 键不存在,触发资源更新检查 + _ = Task.Run(async () => await CheckAndUpdateResourcesAsync(culture)); + } + else + { + // 资源未加载,立即加载 + await LoadResourcesAsync(culture); + + // 重试获取 + if (_resources.TryGetValue(culture, out cultureResources) && + cultureResources.TryGetValue(name, out var value)) + { + return value; + } + } + + _logger.LogWarning("Localization key not found: {Key} for culture: {Culture}", name, culture); + return null; + } + + /// + /// 把本地化文化语言加载到内存中 + /// + /// + /// + public async Task LoadResourcesAsync(string culture) + { + await _semaphore.WaitAsync(); + try + { + // 先尝试从localStorage加载 + if (await TryLoadFromLocalStorage(culture)) + { + // 检查服务器版本,如果需要更新则从服务器加载 + if (await CheckAndUpdateFromServer(culture)) + { + return true; + } + return true; // 本地版本仍然有效 + } + + // 从服务器加载 + return await LoadFromServer(culture); + } + finally + { + _semaphore.Release(); + } + } + + /// + /// 从单例对象中获取版本记录 + /// + /// + /// + private async Task GetResourceVersionAsync(string culture) + { + if (_versions.TryGetValue(culture, out var version)) + { + return version; + } + + var storedVersion = await GetStoredVersion(culture); + return storedVersion; + } + + /// + /// 检查更新资源 + /// + /// + /// + private async Task CheckAndUpdateResourcesAsync(string culture) + { + return await CheckAndUpdateFromServer(culture); + } + + /// + /// 从本地加载文化语言数据 + /// + /// + /// + private async Task TryLoadFromLocalStorage(string culture) + { + try + { + var resourcesJson = await _jsRuntime.InvokeAsync("localStorage.getItem", $"locales_{culture}"); + var version = await _jsRuntime.InvokeAsync("localStorage.getItem", $"locales_version_{culture}"); + + if (string.IsNullOrEmpty(resourcesJson) || string.IsNullOrEmpty(version)) + { + return false; + } + + var resources = JsonSerializer.Deserialize>(resourcesJson); + + if (resources != null && version != null) + { + _resources[culture] = resources; + _versions[culture] = version; + return true; + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to load resources from localStorage for culture: {Culture}", culture); + } + + return false; + } + + /// + /// 从服务器更新获取文化语言数据 + /// + /// + /// + private async Task LoadFromServer(string culture) + { + try + { + var response = await _httpService.Get>($"/api/localeresource/resources/{culture}"); + if (response.Success) + { + _resources[culture] = response.Data.Translations; + _versions[culture] = response.Data.ResourceVersion; + // 保存到localStorage + await SaveToLocalStorage(culture, response.Data.Translations, response.Data.ResourceVersion); + ResourcesUpdated?.Invoke(this, culture); + return true; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to load resources from server for culture: {Culture}", culture); + } + + return false; + } + + + ///
Current count: @currentCount
@Icon
暂无权限可设置
This component demonstrates showing data.
Loading...