Compare commits

2 Commits

Author SHA1 Message Date
mmdi 1b0769da4d working on it and testing 2026-04-29 00:53:30 +03:30
mmdi 8e5efc96ff configed for net9 2026-04-28 13:14:39 +03:30
26 changed files with 2060 additions and 20 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
+203
View File
@@ -0,0 +1,203 @@
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
namespace Mf.Core.Services;
public static class ModelService
{
public static string GetDisplayName(this PropertyInfo property)
{
var displayAttr = property.GetCustomAttribute<DisplayAttribute>();
if (displayAttr == null)
{
return property.Name;
}
return ((displayAttr!.Name != null) ? displayAttr.Name : property.Name);
}
public static Dictionary<T, string> GetEnumDisplayNames<T>() where T : Enum
{
return Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(
e => e,
e => GetDisplayName(e)!
);
}
public static string? GetDisplayName<T>(T enumValue)
{
return typeof(T)
.GetMember(enumValue.ToString())
.FirstOrDefault()?
.GetCustomAttribute<DisplayAttribute>()?
.GetName()
?? enumValue.ToString();
}
public static T GetAttribute<T>(this MemberInfo member, bool isRequired)
where T : Attribute
{
var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();
if (attribute == null && isRequired)
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"The {0} attribute must be defined on member {1}",
typeof(T).Name,
member.Name));
}
return (T)attribute;
}
public static string PropertyDisplayName<T>(Expression<Func<T, object>> propertyExpression)
{
MemberExpression memberExpression = null;
if (propertyExpression.Body is MemberExpression directMember)
{
memberExpression = directMember;
}
else if (propertyExpression.Body is UnaryExpression unary && unary.Operand is MemberExpression unaryMember)
{
memberExpression = unaryMember;
}
if (memberExpression == null)
throw new ArgumentException("Expression is not a valid member access.", nameof(propertyExpression));
var propInfo = memberExpression.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException("Member is not a property.", nameof(propertyExpression));
var displayAttr = propInfo.GetCustomAttribute<DisplayAttribute>();
return displayAttr?.GetName() ?? propInfo.Name;
}
public static PropertyInfo GetProperty<T>(Expression<Func<T, object?>> propertyExpression)
{
MemberExpression memberExpression = null;
if (propertyExpression.Body is MemberExpression directMember)
{
memberExpression = directMember;
}
else if (propertyExpression.Body is UnaryExpression unary && unary.Operand is MemberExpression unaryMember)
{
memberExpression = unaryMember;
}
if (memberExpression == null)
throw new ArgumentException("Expression is not a valid member access.", nameof(propertyExpression));
return memberExpression.Member as PropertyInfo;
}
public static bool ValidateField(object model, string fieldName, object? fieldValue, out List<ValidationResult> results)
{
var context = new ValidationContext(model)
{
MemberName = fieldName
};
results = new List<ValidationResult>();
bool isValid = System.ComponentModel.DataAnnotations.Validator.TryValidateProperty(fieldValue, context, results);
if (!isValid)
{
}
return isValid;
}
public static bool ValidateField<T>(Expression<Func<T, object?>> expression, object? fieldValue, out List<ValidationResult> results)
{
var model = Activator.CreateInstance(typeof(T));
results = new List<ValidationResult>();
if (model == null)
{
return false;
}
string propertyName = GetProperty<T>(expression).Name;
var get = ValidateField(model, propertyName, fieldValue, out var messages);
results = messages;
return get;
}
public static string GetCustomValue(this PropertyInfo propertyInfo, object ob)
{
try
{
var value = propertyInfo.GetValue(ob);
if(value == null)
return string.Empty;
var type = propertyInfo.PropertyType;
var underlyingType = Nullable.GetUnderlyingType(type) ?? type;
// ---------- string ----------
if (underlyingType == typeof(string))
return value.ToString()!;
// ---------- int / int? ----------
if (underlyingType == typeof(int))
return value.ToString()!;
// // ---------- long / price ----------
// if (underlyingType == typeof(long))
// {
// var priceAttr = propertyInfo.GetCustomAttribute<PriceAttriute>();
// if (priceAttr != null)
// return ((long)value).ToString(priceAttr.Format);
//
// return value.ToString()!;
// }
// ---------- Guid / Guid? ----------
if (underlyingType == typeof(Guid))
return value.ToString()!;
// ---------- DateTime / DateTime? ----------
if (underlyingType == typeof(DateTime))
return ((DateTime)value).ToShamsi();
string? res = propertyInfo.PropertyType.Name switch
{
"String" => propertyInfo.GetValue(ob).ToString(),
"Guid" => (propertyInfo.GetValue(ob) as Nullable<Guid>).ToString(),
"DateTime" => (propertyInfo.GetValue(ob) as Nullable<DateTime>)!.Value.ToShamsi(),
_ => null
};
if (res != null)
{
return res;
}
if (propertyInfo.GetValue(ob) is object baseValue)
{
if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) && propertyInfo.PropertyType.GetGenericArguments()[0] == typeof(DateTime))
{
return (baseValue as Nullable<DateTime>)!.Value.ToShamsi();
}
// else if (propertyInfo.GetCustomAttribute<PriceAttriute>() is PriceAttriute priceAttr)
// {
//
// return ((long)baseValue).ToString(priceAttr.Format);
// }
}
res = "";
return res;
}
catch
{
return "";
}
}
}
+121
View File
@@ -0,0 +1,121 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Mf.Core.SubEntities.Auth
{
[Table]
public class Role : BaseEntity
{
[DisplayTableTitle]
public string? RoleTitle { get; set; }
public string? RoleName { get; set; }
public List<RoleAccess>? RoleAccesses { get; set; }
}
[Table]
public class RoleAccess : BaseEntity
{
public long RoleId { get; set; }
public AdminPermission Permission { get; set; }
public PermissionAction PermissionAction { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
#region Relations
[ForeignKey("RoleId")]
public Role? Role { get; set; }
#endregion
}
public enum AdminPermission
{
[ParentCode(Dashbord)]
Dashbord,
[ParentCode(Users)]
Users,
[ParentCode(Orders)]
Orders,
[ParentCode(Products)]
Products,
[ParentCode(BaseTables)]
BaseTables,
[ParentCode(AdminManager)]
AdminManager,
[ParentCode(AdminManager)]
AdminUsers,
[ParentCode(AdminManager)]
RoleManager,
[ParentCode(AdminManager)]
AccessRoleManager,
[ParentCode(Logs)]
Logs,
[ParentCode(Logs)]
InventoryLog,
[ParentCode(Tags)]
Tags,
[ParentCode(CityAdnProvinces)]
CityAdnProvinces
}
[Flags]
public enum PermissionAction
{
List = 1 << 0,
Create = 1 << 1,
Edit = 1 << 2,
Delete = 1 << 3
}
[AttributeUsage(AttributeTargets.Field)]
public class ParentCodeAttribute : Attribute
{
public AdminPermission Parent { get; set; }
public ParentCodeAttribute(AdminPermission parent)
{
Parent = parent;
}
}
public static class RolePermissionService
{
public static bool HasPermissionAction(PermissionAction permission, PermissionAction sub)
{
if ((permission & sub) == sub)
{
return true;
}
return false;
}
}
}
+46
View File
@@ -0,0 +1,46 @@
using Mf.Core.SubEntities.Auth;
namespace Mf.Core.SubEntities
{
//tables in data base
public class TableAttribute : Attribute
{
public string? gid { get; set; }
}
public class AdminPermissions : Attribute
{
private AdminPermission[] _permissions;
public AdminPermissions(params AdminPermission[] permission)
{
this._permissions = permission;
}
public AdminPermission[] GetPermissions()
{
return _permissions;
}
}
//price format
public class PriceAttriute : Attribute
{
public string? Format { get; set; } = "N0";
}
//picture format
public class PictureAttribute : Attribute
{
}
//html format read by (markuptext) in blazor
public class HtmlFormatAttribute : Attribute
{
}
//string property of class for display of the class
public class DisplayTableTitleAttribute : Attribute
{
}
}
+3 -3
View File
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Mf.Core\Mf.Core.csproj" />
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -8,5 +8,6 @@ namespace Mf.Razor.Entity.Interfaces
{
public interface IServiceLocator
{
string Token { get; }
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
+152
View File
@@ -0,0 +1,152 @@
using System.Linq.Expressions;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Mf.Core.SubEntities.QueryModel;
namespace Mf.Razor.Entity.Services;
public static class GenericQuery
{
public static async Task<List<object>?> GetTable(this HttpClient httpClient, Type tableType, string token)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var result = await httpClient.PostAsJsonAsync("api/generic/get", new QueryRequest { ObjectType = tableType.AssemblyQualifiedName });
return await result.Content.ReadFromJsonAsync<List<object>>();
}
public static async Task<List<IEntity>?> GetTable<IEntity>(this HttpClient httpClient, string token)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var result = await httpClient.PostAsJsonAsync("api/generic/get", new QueryRequest { ObjectType = typeof(IEntity).AssemblyQualifiedName });
return await result.Content.ReadFromJsonAsync<List<IEntity>>();
}
public static async Task<object?> GetItemAsync(this HttpClient httpClient, Type type, long Id)
{
var result = await httpClient.PostAsJsonAsync("api/generic/getitem", new { typeId = type.AssemblyQualifiedName, id = Id });
return await result.Content.ReadFromJsonAsync(type);
}
public static async Task<TEntity> GetItemAsync<TEntity>(this HttpClient httpClient, long Id)
{
return (TEntity)await httpClient.GetItemAsync(typeof(TEntity), Id);
}
public static async Task<object?> GetItemAsync(this HttpClient httpClient, Type type, Guid Guid)
{
var result = await httpClient.PostAsJsonAsync("api/generic/getitem", new { typeId = type.AssemblyQualifiedName, gid = Guid });
return await result.Content.ReadFromJsonAsync(type);
}
public static async Task<TEntity> GetItemAsync<TEntity>(this HttpClient httpClient, Guid Guid)
{
return (TEntity)await httpClient.GetItemAsync(typeof(TEntity), Guid);
}
public static async Task<HttpResponseMessage> InsertAsync(this HttpClient httpClient, Type type, object data)
{
AlterRequest req = new AlterRequest() { Model = data, ObjectType = type.AssemblyQualifiedName };
return await httpClient.PostAsJsonAsync("api/generic/insert", req);
}
public static async Task<HttpResponseMessage> InsertAsync<T>(this HttpClient httpClient, T model)
{
AlterRequest req = new AlterRequest() { Model = model, ObjectType = typeof(T).AssemblyQualifiedName };
return await httpClient.PostAsJsonAsync("api/generic/insert", req);
}
public static async Task<HttpResponseMessage> UpdateAsync<T>(this HttpClient httpClient, T model)
{
AlterRequest req = new AlterRequest() { Model = model, ObjectType = typeof(T).AssemblyQualifiedName };
return await httpClient.PostAsJsonAsync("api/generic/update", req);
}
public static QueryRequest Where<TEntity>(Expression<Func<TEntity, bool>> expression)
{
QueryRequest _request = new();
_request.ObjectType = typeof(TEntity).AssemblyQualifiedName;
if (expression.Body is BinaryExpression binary)
{
var left = GetPropertyName((MemberExpression)binary.Left);
var op = GetOperator(binary.NodeType);
var right = Expression.Lambda(binary.Right).Compile().DynamicInvoke()?.ToString();
_request.Filters.Add(new Filter
{
Field = left,
Operator = op,
Value = right
});
}
else if (expression.Body is MethodCallExpression methodCall)
{
var method = methodCall.Method.Name;
if (method is "Contains" or "StartsWith" or "EndsWith")
{
var field = GetPropertyName(methodCall.Object ?? methodCall.Arguments[0]);
var value = Expression.Lambda(methodCall.Arguments[^1]).Compile().DynamicInvoke()?.ToString();
_request.Filters.Add(new Filter
{
Field = field,
Operator = method, // توی سرور بررسی کن با این نام یا تبدیل کن
Value = value
});
}
else
{
throw new NotSupportedException($"Unsupported method: {method}");
}
}
return _request;
}
public static async Task<List<object>> FetchToListAsync(this HttpClient httpClient, Type type, QueryRequest? query)
{
if (query == null)
query = new();
var result = await httpClient.PostAsJsonAsync("api/generic/get", query);
return await result.Content.ReadFromJsonAsync<List<object>>();
}
private static string GetOperator(ExpressionType type)
{
return type switch
{
ExpressionType.Equal => "=",
ExpressionType.GreaterThan => ">",
ExpressionType.GreaterThanOrEqual => ">=",
ExpressionType.LessThan => "<",
ExpressionType.LessThanOrEqual => "<=",
ExpressionType.NotEqual => "!=",
_ => throw new NotSupportedException("Unsupported operator")
};
}
private static string GetPropertyName(Expression expression)
{
return expression switch
{
MemberExpression me => me.Member.Name,
UnaryExpression ue when ue.Operand is MemberExpression me => me.Member.Name,
_ => throw new NotSupportedException("Unsupported expression")
};
}
public static string GetImgSrc(this HttpClient httpClient, string imgName)
{
return httpClient.BaseAddress + "api/book-image/" + imgName + "?width=50&&height=50";
}
}
+223
View File
@@ -0,0 +1,223 @@
@using System.Reflection
@inherits BaseForm
<h3>Alter</h3>
<EditForm OnValidSubmit="Success" Model="Type">
<div>
<DataAnnotationsValidator />
<ValidationSummary />
@foreach (var p in Properties)
{
@if (p.PropertyType.Name == "Int32")
{
<div class="form-floating mb-3">
<InputNumber id="@nameof(p.Name)" @bind-Value="intValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
else if (p.PropertyType.Name == "String")
{
<div class="form-floating mb-3">
<InputText id="@nameof(p.Name)" @bind-Value="Values.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
else if (p.PropertyType == typeof(Nullable<int>))
{
<div class="form-floating mb-3">
<InputNumber id="@nameof(p.Name)" @bind-Value="n_intValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
else if (p.PropertyType.Name == "DateTime")
{
<div class="form-floating mb-3">
<input id="@nameof(p.Name)" @bind="DateTimeValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
else if (p.PropertyType.Name == "Boolean")
{
<div class="form-floating mb-3">
<input id="@nameof(p.Name)" @bind="BoolValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
}
</div>
<button class='btn btn-sm @((IsUpdate)? "btn-warning" : "btn-success")'>ثبت</button>
</EditForm>
@code {
[Parameter]
public Type Type { get; set; } /* = typeof(SubAsil.Models.Users.User); */
[Parameter]
public object Data { get; set; }
[Parameter]
public EventCallback<object> OnChange { get; set; }
[Parameter]
public EventCallback<object> OnDone { get; set; }
public List<PropertyInfo> Properties { get; set; } = new();
List<Mmodel<bool?>> BoolValues = new();
List<Mmodel<string>> Values = new();
List<Mmodel<DateTime?>> DateTimeValues = new();
List<Mmodel<int>> intValues = new();
List<Mmodel<int?>> n_intValues = new();
List<bool> boolValues = new();
object model = new object();
bool IsUpdate = false;
protected override async Task OnInitializedAsync()
{
model = Activator.CreateInstance(Type);
if (Data != null)
{
model = Data;
IsUpdate = true;
}
//var Users = await HttpClient.GetFromJsonAsync<List<object>>("api/generic/get?id=" + Type.AssemblyQualifiedName);
// var user = Users[0];
// model = user;
Properties = Type.GetProperties().ToList();
Properties = Properties.Where(p => !PropertyFilters.Any(pp => pp == p.Name)).ToList();
foreach (var p in Properties)
{
string typeName = p.PropertyType.Name;
// ServiceLocator.Log(p.PropertyType.ToString());
// ServiceLocator.Log(typeName);
if (p.PropertyType == typeof(Nullable<int>))
{
n_intValues.Add(new Mmodel<int?>() { Value = (p.GetValue(model) != null) ? int.Parse(p.GetValue(model).ToString()) : null, PName = p.Name });
}
// if (p.PropertyType == typeof(Nullable<DateTime>))
// {
// DateTimeValues.Add(new Mmodel<DateTime?>() { Value = (p.GetValue(model) != null) ? Convert.ToDateTime(p.GetValue(model)) : null, PName = p.Name });
// }
if (typeName == "DateTime")
{
DateTimeValues.Add(new Mmodel<DateTime?>() { Value = (p.GetValue(model) != null) ? Convert.ToDateTime(p.GetValue(model)) : null, PName = p.Name });
}
if (typeName == "Boolean")
{
BoolValues.Add(new Mmodel<bool?>() { Value = (p.GetValue(model) != null) ? Convert.ToBoolean(p.GetValue(model)) : null, PName = p.Name });
}
// if (typeName.Contains("Nullable"))
// {
// if (p.PropertyType.UnderlyingSystemType.ToString().Contains("Int32"))
// {
// n_intValues.Add(new Mmodel<int?>() { Value = (p.GetValue(model) != null) ? int.Parse(p.GetValue(model).ToString()) : null, PName = p.Name });
// }
// }
if (p.PropertyType.Name == "String")
{
Values.Add(new Mmodel<string>() { Value = (string)p.GetValue(model), PName = p.Name });
}
else if (p.PropertyType.Name == "Int32")
{
intValues.Add(new Mmodel<int>() { Value = int.Parse(p.GetValue(model).ToString()), PName = p.Name });
}
}
}
[Inject]
public IJSRuntime Js { get; set; }
async void Success()
{
//await Js.Alert(model.ToString());
foreach (var item in Properties)
{
if (item.PropertyType == typeof(string))
{
item.SetValue(model, Values.Where(v => v.PName == item.Name).Single().Value);
}
if (item.PropertyType == typeof(Int32))
{
item.SetValue(model, intValues.Where(v => v.PName == item.Name).Single().Value);
}
if (item.PropertyType == typeof(Nullable<Int32>))
{
item.SetValue(model, n_intValues.Where(v => v.PName == item.Name).Single().Value);
}
if (item.PropertyType == typeof(Nullable<DateTime>))
{
var find = DateTimeValues.Where(v => v.PName == item.Name).SingleOrDefault()?.Value;
item.SetValue(model, DateTimeValues.Where(v => v.PName == item.Name).SingleOrDefault()?.Value);
}
}
if (IsUpdate)
{
//var result = HttpClient.PostAsJsonAsync<PostModel>("api/generic/update", new PostModel() { type = this.Type.AssemblyQualifiedName, model = this.model });
}
else
{
//var result = HttpClient.PostAsJsonAsync<PostModel>("api/generic/add", new PostModel() { type = this.Type.AssemblyQualifiedName, model = this.model });
}
await OnDone.InvokeAsync(model);
//await OnChange.InvokeAsync(model);
}
public int GetNumber(PropertyInfo property)
{
return Properties.IndexOf(property);
}
public class Mmodel<T>
{
public T Value { get; set; }
public string PName { get; set; }
}
public List<string> PropertyFilters = new List<string> { "CreateDate", "UpdateDate" };
}
+178
View File
@@ -0,0 +1,178 @@
@using System.Reflection
@inherits BaseForm
<EditForm OnValidSubmit="Success" Model="model">
<div>
<DataAnnotationsValidator />
<ValidationSummary />
@foreach (var p in Properties)
{
@if (p.PropertyType.Name == "Int32")
{
<DynamicBinder T="int"
Getter="@(() => GetProperty<int>(p))"
Setter="@(v => p.SetValue(model, v))">
<ChildContent Context="item">
<div class="form-floating mb-3">
<InputNumber TValue="int" class="form-control"
id="@nameof(p.Name)"
Value="@item.Value"
ValueChanged="@(v => item.SetValue(v))"
ValueExpression="@(() => item.Value)" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
</ChildContent>
</DynamicBinder>
}
else if (p.PropertyType.Name == "String")
{
<DynamicBinder T="string"
Getter="@(() => GetProperty<string>(p))"
Setter="@(v => p.SetValue(model, v))">
<ChildContent Context="item">
<div class="form-floating mb-3">
<InputText class="form-control"
id="@nameof(p.Name)"
Value="@item.Value"
ValueChanged="@(v => item.SetValue(v))"
ValueExpression="@(() => item.Value)" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
</ChildContent>
</DynamicBinder>
}
else if (p.PropertyType.Name == "DateTime")
{
<DynamicBinder T="DateTime"
Getter="@(() => GetProperty<DateTime>(p))"
Setter="@(v => p.SetValue(model, v))">
<ChildContent Context="item">
<div class="form-floating mb-3">
<Cdate TValue="DateTime" class="form-control"
id="@nameof(p.Name)"
Value="@item.Value"
ValueChanged="@(v => item.SetValue(v))"
ValueExpression="@(() => item.Value)" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
</ChildContent>
</DynamicBinder>
}
@* else if (p.PropertyType == typeof(Nullable<int>))
{
<div class="form-floating mb-3">
<InputNumber id="@nameof(p.Name)" @bind-Value="n_intValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
else if (p.PropertyType.Name == "DateTime")
{
<div class="form-floating mb-3">
<input id="@nameof(p.Name)" @bind="DateTimeValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
}
else if (p.PropertyType.Name == "Boolean")
{
<div class="form-floating mb-3">
<input id="@nameof(p.Name)" @bind="BoolValues.Where(v => v.PName == p.Name).Single().Value" placeholder="placeholder" class="form-control" />
<label for="@nameof(p.Name)">@p.GetDisplayName()</label>
</div>
} *@
}
</div>
<button class='btn btn-sm @((IsUpdate)? "btn-warning" : "btn-success")'>ثبت</button>
</EditForm>
@code {
[Parameter]
public Type Type { get; set; } /* = typeof(SubAsil.Models.Users.User); */
[Parameter]
public object Data { get; set; }
[Parameter]
public EventCallback<object> OnChange { get; set; }
[Parameter]
public EventCallback<object> OnDone { get; set; }
//public object DynamicInstance { get; set; } = new();
public List<PropertyInfo> Properties { get; set; } = new();
object model = new object();
bool IsUpdate = false;
T? GetProperty<T>(PropertyInfo prop)
{
return (T)prop.GetValue(model)!;
}
void SetValue<T>(PropertyInfo prop, T value)
{
prop.SetValue(model, value);
}
protected override async Task OnInitializedAsync()
{
model = Activator.CreateInstance(Type);
if (Data != null)
{
model = Data;
//IsUpdate = true;
}
var sssss = (BaseEntity)model;
// sssss.CreateDate = DateTime.Now;
model = sssss;
Properties = Type.GetProperties().ToList();
Properties = Properties.Where(p => !PropertyFilters.Any(pp => pp == p.Name)).ToList();
}
[Inject]
public IJSRuntime Js { get; set; }
async void Success()
{
await OnDone.InvokeAsync(model);
//await OnChange.InvokeAsync(model);
}
public int GetNumber(PropertyInfo property)
{
return Properties.IndexOf(property);
}
public class Mmodel<T>
{
public T Value { get; set; }
public string PName { get; set; }
}
public List<string> PropertyFilters = new List<string> { /* "CreateDate", "UpdateDate" */ };
}
+55
View File
@@ -0,0 +1,55 @@
@using System.Globalization
@inject IJSRuntime JS
@typeparam TValue
@inherits Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
<input id="@_inputId"
type="text"
class="form-control"
value="@CurrentPersianDate"
@ref="_inputRef"
readonly />
@code {
private static readonly PersianCalendar _pc = new();
private ElementReference _inputRef;
private string _inputId = "persianDateInput_" + Guid.NewGuid().ToString("N");
private string CurrentPersianDate => FormatDate(Value);
[JSInvokable]
public Task OnPersianDateSelected(string gregorianDate)
{
if (DateTime.TryParse(gregorianDate, out var dt))
{
CurrentValue = (TValue)(object)dt;
}
return Task.CompletedTask;
}
private string FormatDate(object value)
{
if (value is DateTime dt && dt >= new DateTime(622, 3, 22))
{
return $"{_pc.GetYear(dt)}/{_pc.GetMonth(dt):00}/{_pc.GetDayOfMonth(dt):00}";
}
return string.Empty;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("initPersianDatePicker", _inputId, DotNetObjectReference.Create(this));
}
}
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
result = default;
validationErrorMessage = null;
return true; // این بخش مهم نیست چون ورودی مستقیماً از JS گرفته می‌شه
}
}
+98
View File
@@ -0,0 +1,98 @@
@using System.Reflection
@inherits BaseForm
@inject Mf.Razor.Entity.Interfaces.IServiceLocator serviceLocator
@inject HttpClient httpClient
@foreach(var p in Properties)
{
<p>@p.GetDisplayName() : @GetCustomValue(p,model)</p>
}
@code {
[Inject]
public IJSRuntime Js { get; set; }
[Parameter]
public Type Type { get; set; }
[Parameter]
public long Id { get; set; }
public List<string> PropertyFilters = new List<string> { /* "CreateDate", "UpdateDate" */ };
public List<PropertyInfo> Properties { get; set; } = new();
object model = new object();
T? GetProperty<T>(PropertyInfo prop)
{
return (T)prop.GetValue(model)!;
}
void SetValue<T>(PropertyInfo prop, T value)
{
prop.SetValue(model, value);
}
protected override async Task OnInitializedAsync()
{
model = await httpClient.GetItemAsync(Type, Id);
Properties = Type.GetProperties().ToList();
Properties = Properties.Where(p => !PropertyFilters.Any(pp => pp == p.Name)).ToList();
}
public int GetNumber(PropertyInfo property)
{
return Properties.IndexOf(property);
}
string? GetCustomValue(PropertyInfo propertyInfo, object ob)
{
try
{
if (propertyInfo.PropertyType.IsGenericType &&
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
propertyInfo.PropertyType.GetGenericArguments()[0] == typeof(DateTime))
{
return (propertyInfo.GetValue(ob) as Nullable<DateTime>)!.Value.ToShamsi();
}
var bb = propertyInfo.PropertyType.Name;
string? res = propertyInfo.PropertyType.Name switch
{
"Guid" => (propertyInfo.GetValue(ob) as Nullable<Guid>).ToString(),
"DateTime" => (propertyInfo.GetValue(ob) as Nullable<DateTime>)!.Value.ToShamsi(),
_ => propertyInfo.GetValue(ob)?.ToString()
};
return res;
}
catch
{
return null;
}
}
}
@@ -0,0 +1,64 @@
@using System.Reflection
@inherits BaseForm
@inject HttpClient httpClient
<span>@DisplayName</span>
@code {
[Parameter]
public Type Type { get; set; }
[Parameter]
public long Id { get; set; }
private string DisplayName = "";
public List<string> PropertyFilters = new List<string> { /* "CreateDate", "UpdateDate" */ };
object? model = new object();
T? GetProperty<T>(PropertyInfo prop)
{
return (T)prop.GetValue(model)!;
}
void SetValue<T>(PropertyInfo prop, T value)
{
prop.SetValue(model, value);
}
protected override async Task OnInitializedAsync()
{
model = await httpClient.GetItemAsync(Type, Id);
string? result = null;
if(model != null)
{
try
{
result = Type.GetProperties()!.Where(t => t.GetCustomAttribute<DisplayTableTitleAttribute>() != null).SingleOrDefault()!.GetValue(model)!.ToString();
}
catch (Exception ex)
{
}
}
if(result != null)
{
DisplayName = result;
}
else
{
DisplayName = Id.ToString();
}
}
}
+36
View File
@@ -0,0 +1,36 @@
@* @using System.Reflection *@
@* @inherits BaseForm *@
@* @inject HttpClient httpClient *@
@* *@
@* *@
@* <span>@DisplayName</span> *@
@* *@
@* *@
@* @code { *@
@* [Parameter] *@
@* public Guid Id { get; set; } *@
@* *@
@* private string DisplayName = ""; *@
@* *@
@* public List<string> PropertyFilters = new List<string> { /* "CreateDate", "UpdateDate" */ }; *@
@* *@
@* User user = new User(); *@
@* *@
@* *@
@* *@
@* *@
@* protected override async Task OnInitializedAsync() *@
@* { *@
@* user = await httpClient.GetItemAsync<User>(Id); *@
@* *@
@* if (user != null) *@
@* { *@
@* DisplayName = user.DisplayName; *@
@* } *@
@* else *@
@* { *@
@* DisplayName = Id.ToString(); *@
@* } *@
@* *@
@* } *@
@* } *@
+24
View File
@@ -0,0 +1,24 @@
@typeparam T
@ChildContent(ValueWrapper)
@code {
[Parameter] public Func<T> Getter { get; set; }
[Parameter] public Action<T> Setter { get; set; }
[Parameter] public RenderFragment<BindableValue<T>> ChildContent { get; set; }
private BindableValue<T> ValueWrapper => new BindableValue<T>
{
Value = Getter(),
SetValue = Setter
};
}
@code {
public class BindableValue<T>
{
public T Value { get; set; }
public Action<T> SetValue { get; set; }
}
}
+231
View File
@@ -0,0 +1,231 @@
@inherits BaseForm
@using System.Reflection
@using System.ComponentModel.DataAnnotations
@using System.Net.Http.Json
@using Mf.Core.SubEntities.QueryModel
@using Mf.Razor.Services
@inject HttpClient httpClient
@inject IJSRuntime Js
<p>tableName</p>
<div>
<button class="btn btn-sm btn-success" @onclick="Insert">افزودن</button>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
@foreach (var p in Properties)
{
<th>@p.GetDisplayName()</th>
}
<th>actions</th>
</tr>
</thead>
<tbody>
@foreach (var ob in Info)
{
<tr>
@foreach (var p in Properties)
{
<td class="nowrap-col">@GetCustomValue(p, ob)</td>
}
<td>
<div class="btn-group">
<button @onclick="() => Update(ob)" class="btn btn-sm btn-warning">ویرایش</button>
<button @onclick="() => Delete(ob)" class="btn btn-sm btn-danger">حذف</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
@code {
[Parameter]
public Type Type { get; set; }
public List<PropertyInfo> Properties { get; set; } = new List<PropertyInfo>();
public List<object> Data { get; set; } = new List<object>();
public List<object> Info { get; set; } = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await LoadData();
}
async Task LoadData()
{
// LoadingOn();
Data = new List<object>();
Info = new List<object>();
// typeName = await HttpClient.GetStringAsync("api/tables/getTypeByGid?id=" + typegid);
var getData = await httpClient.PostAsJsonAsync("api/generic/get", new QueryRequest { ObjectType = Type.AssemblyQualifiedName });
Data = await getData.Content.ReadFromJsonAsync<List<object>>();
//Data = await httpClient.GetFromJsonAsync<List<object>>("api/generic/get?id=" + Type.AssemblyQualifiedName);
Properties = Type.GetProperties().ToList();
foreach (var b in Data)
{
object model = Activator.CreateInstance(Type);
var getName = model as BaseEntity;
var sssss = Newtonsoft.Json.JsonConvert.DeserializeObject(b.ToString(), Type);
Info.Add(sssss);
var sss = Properties[0].GetValue(sssss);
//await JsRuntime.Alert("addddddd");
}
//LoadingOff();
}
void CallBack()
{
}
async void Insert()
{
ServiceLocator.AddWidget(new RenderFragment(builder =>
{
builder.OpenComponent(0, typeof(AlterT));
builder.AddAttribute(0, "Type", Type);
builder.AddAttribute(2, "OnChange", EventCallback.Factory.Create<object>(this, async (parameter) => { await LoadData(); ServiceLocator.Update(); }));
builder.AddAttribute(3, "OnDone", EventCallback.Factory.Create<object>(this, async (parameter) => { await DoInsert(new AlterRequest { Model = parameter, ObjectType = Type.AssemblyQualifiedName }); }));
builder.CloseComponent();
}), "افزودن");
}
async void Update(object item)
{
ServiceLocator.AddWidget(new RenderFragment(builder =>
{
builder.OpenComponent(0, typeof(AlterT));
builder.AddAttribute(0, "Type", Type);
builder.AddAttribute(1, "Data", item);
builder.AddAttribute(2, "OnChange", EventCallback.Factory.Create<object>(this, async (parameter) => { await LoadData(); ServiceLocator.Update(); }));
builder.AddAttribute(3, "OnDone", EventCallback.Factory.Create<object>(this, async (parameter) => { await DoUpdate(new AlterRequest { Model = parameter, ObjectType = Type.AssemblyQualifiedName }); }));
builder.CloseComponent();
}), "ویرایش");
}
async void Delete(object item)
{
if (await Js.InvokeAsync<bool>("confirm"))
{
AlterRequest model = new() { Model = item, ObjectType = Type.AssemblyQualifiedName };
await httpClient.PostAsJsonAsync<AlterRequest>("api/generic/delete", model);
await Task.Delay(TimeSetting.AfterInsertOrDefault);
await LoadData();
}
}
async Task DoUpdate(AlterRequest model)
{
var result = httpClient.PostAsJsonAsync<AlterRequest>("api/generic/update", model);
await Task.Delay(TimeSetting.AfterInsertOrDefault);
await LoadData();
ServiceLocator.Update();
}
async Task DoInsert(AlterRequest model)
{
var result = httpClient.PostAsJsonAsync<AlterRequest>("api/generic/insert", model);
await Task.Delay(TimeSetting.AfterInsertOrDefault);
await LoadData();
ServiceLocator.Update();
}
//TODO : fix it
string? GetCustomValue(PropertyInfo propertyInfo, object ob)
{
try
{
//ServiceLocator.Log($"{propertyInfo.Name} : {propertyInfo.PropertyType.Name}" );
string? res = propertyInfo.PropertyType.Name switch
{
"Int64" => propertyInfo.GetValue(ob)!.ToString(),
"String" => propertyInfo.GetValue(ob)!.ToString(),
"Guid" => (propertyInfo.GetValue(ob) as Nullable<Guid>).ToString(),
"DateTime" => (propertyInfo.GetValue(ob) as Nullable<DateTime>)!.Value.ToShamsi(),
_ => null
};
if (res != null)
{
return res;
}
if (propertyInfo.GetValue(ob) is object baseValue)
{
if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) && propertyInfo.PropertyType.GetGenericArguments()[0] == typeof(DateTime))
{
return (baseValue as Nullable<DateTime>)!.Value.ToShamsi();
}
else if (propertyInfo.GetCustomAttribute<PriceAttriute>() is PriceAttriute priceAttr)
{
((long)baseValue).ToString(priceAttr.Format);
}
}
//var bb = propertyInfo.PropertyType.Name;
return res;
}
catch
{
return null;
}
}
}
@@ -0,0 +1,93 @@
@inherits BaseForm
@using Entities.Entities.Static
@inject HttpClient http
@if (IsLoading)
{
<Loadingspinner />
}
else
{
<div class="form-floating mb-3">
<select class="form-select" id="province" @oninput="ProvinceChange" aria-label="Floating label select example">
<option value="0">همه</option>
@foreach (var p in Provinces)
{
<option value="@p.Id">@p.ProvinceName</option>
}
</select>
<label for="province">استان</label>
</div>
<div class="form-floating mb-3">
<SimpleBlazorMultiselect.SimpleMultiselect DefaultText="شهر" Class="font-medium" Options="(SelectProvinceId == 0 )? Cities : Cities.Where(c => c.ProvinceId == SelectProvinceId).ToList()" IsMultiSelect="false" TItem="City" StringSelector="b => b.CityName" CanFilter="true" @bind-SelectedOptions="selectCity" >
<SelectedOptionsRenderer Context="options">
@foreach (var item in options)
{
<span class="badge bg-primary font-medium"
style="padding: 6px; margin-right: 10px;">
@item.CityName
</span>
}
</SelectedOptionsRenderer>
</SimpleBlazorMultiselect.SimpleMultiselect>
</div>
}
@code {
[Parameter]
public List<Province> Provinces { get; set; } = new();
[Parameter]
public List<City> Cities { get; set; } = new();
[Parameter]
public City? Value { get; set; }
[Parameter]
public EventCallback<City> ValueChanged { get; set; }
private int SelectProvinceId = 0;
private HashSet<City> _selectCity = new();
private HashSet<City> selectCity { get { return _selectCity; } set { _selectCity = value; Value = value.FirstOrDefault(); ValueChanged.InvokeAsync(Value); } }
protected override async Task LoadDataAsync()
{
if (Provinces.Count() == 0)
{
Provinces = await http.GetTable<Province>();
}
if (Cities.Count() == 0)
{
Cities = await http.GetTable<City>();
}
if(Value != null)
{
selectCity.Add(Value);
}
await base.LoadDataAsync();
}
private void ProvinceChange(ChangeEventArgs e)
{
SelectProvinceId = int.Parse(e!.Value!.ToString()!);
}
}
+155
View File
@@ -0,0 +1,155 @@
@* <div class="modal fade show" id="exampleModalScrollable" tabindex="-1" aria-labelledby="exampleModalScrollableTitle" style="display: block;" aria-modal="true" role="dialog">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalScrollableTitle">Modal title</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
</button>
</div>
<div class="modal-body"><p>This is some placeholder content to show the scrolling behavior for modals. We use repeated line breaks to demonstrate how content can exceed minimum inner height, thereby showing inner scrolling. When content becomes longer than the predefined max-height of modal, content will be cropped and scrollable within the modal.</p><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><p>This content should appear at the bottom after you scroll.</p></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button><button type="button" class="btn btn-primary">Save changes</button></div>
</div>
</div>
</div> *@
<div class="modal fade @modalStyle()" id="exampleModalScrollable" tabindex="-1" aria-labelledby="exampleModalScrollableTitle" style='@((IsVisible == true)?"display:block": "display:none" )' aria-modal="true" role="dialog" >
<div class="modal-dialog modal-dialog-scrollable ">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalScrollableTitle">@Title</h1>
<button type="button" @onclick="() => Close()" class="btn-close" >
</button>
</div>
<div class="modal-body">
@ChildContent
</div>
@* <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button><button type="button" class="btn btn-primary">Save changes</button>
</div> *@
</div>
@* <div class="action-pannel">
<button class="btn btn-danger btn-modal" @onclick="() => Close()"></button>
@if (!MinimizeMode)
{
if (!FixMode)
{
<button @onclick="async () => await Fs()" style="" class="btn btn-warning btn-modal"></button>
}
<button style="" class="btn btn-success btn-modal" @onclick="() => Fix()"></button>
}
@if (!FixMode)
{
<button style="" class="btn btn-modal" @onclick="() => Minimize()"></button>
}
<span>@Title</span>
</div>
<div class="modal-content">
@ChildContent
</div> *@
</div>
</div>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public Guid componentId { get; set; }
[Parameter]
public string? Title { get; set; }
[Parameter]
public bool FullScreanMode { get; set; }
private bool IsVisible => componentId == ServiceLocator.LastModalId;
public List<string> modalClassList = new();
public string modalStyle()
{
string r = "";
foreach (var c in modalClassList)
{
r += c;
r += " ";
}
return r;
}
public string idName { get; set; } = "";
public string datetime;
protected override async Task OnInitializedAsync()
{
datetime = DateTime.Now.ToString();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
// if (FullScreanMode == true)
// {
// await Fs();
// }
await Task.Delay(50);
modalClassList.Add("show");
ServiceLocator.Update();
}
}
// [Parameter]
// public EventCallback<Guid> eventCallback { get; set; }
async void Close()
{
StateHasChanged();
await Task.Delay(100);
ChildContent = null;
var item = ServiceLocator.pageContentComponents.Where(p => p.Gid == componentId).Single();
ServiceLocator.pageContentComponents.Remove(item);
StateHasChanged();
ServiceLocator.Update();
}
[Inject]
public IJSRuntime Js { get; set; }
async void Call()
{
await Js.Alert("Call in the modal");
}
}
+237
View File
@@ -0,0 +1,237 @@
@using System.Reflection
@using Mf.Core
@using System.ComponentModel.DataAnnotations.Schema
@typeparam TItem
@inject HttpClient httpClient
@inject IJSRuntime Js
@inherits BaseForm
@if (IsLoading)
{
<Loadingspinner />
}
else
{
<th colspan="@Columns.Count">
<input type="text" class="form-control" placeholder="جستجو..." @bind="SearchText" @bind:event="oninput" />
</th>
<div class="table-container">
<div class="table-scroll">
<table class="table table-bordered table-hover">
<thead>
<tr>
@foreach (var column in Columns)
{
<th class='nowrap-col ' @onclick="() => SortBy(column.Name)">
@column.GetDisplayName()
@if (SortColumn == column.Name)
{
<span>@(SortAscending ? "▲" : "▼")</span>
}
</th>
}
<th class="sticky-col-right">عملیات</th>
</tr>
</thead>
<tbody>
@foreach (var item in PagedData)
{
<tr>
@foreach (var column in Columns)
{
@if (column.GetCustomAttribute<HtmlFormatAttribute>() != null && GetPropertyValue(item, column.Name) != null)
{
<td>
@if (((MarkupString)GetPropertyValue(item!, column.Name)!.ToString()!) is MarkupString content)
{
<button style="text-overflow:ellipsis;overflow:hidden;max-width:100px;max-height:50px;white-space:nowrap;" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="@content">@content</button>
}
</td>
}
else if (column.GetCustomAttribute<PictureAttribute>() != null)
{
<td>
<img src='@(httpClient.BaseAddress + "api/book-image/" + @GetPropertyValue(item, column.Name) + "?width=50&&height=50")' />
</td>
}
@* else if (column.PropertyType == typeof(User)) *@
@* { *@
@* <td class="nowrap-col"> *@
@* <button class="btn"> *@
@* <DisplayUser Id="@Guid.Parse(GetPropertyValue(item, column.GetCustomAttribute<ForeignKeyAttribute>().Name).ToString())" /> *@
@* </button> *@
@* </td> *@
@* *@
@* } *@
else if (column.GetCustomAttribute<ForeignKeyAttribute>() != null)
{
@if (GetPropertyValue(item, column.GetCustomAttribute<ForeignKeyAttribute>().Name) != null)
{
<td class="nowrap-col">
<button class="btn">
<DisplayTableName Type="@column.PropertyType" Id="@long.Parse(GetPropertyValue(item, column.GetCustomAttribute<ForeignKeyAttribute>().Name).ToString())" />
</button>
@* <Detail Type="@column.PropertyType" Id="@long.Parse(GetPropertyValue(item, column.GetCustomAttribute<ForeignKeyAttribute>().Name).ToString())" /> *@
</td>
}
else
{
<td></td>
}
}
else if (column.GetType().Name == "Guid")
{
<td>
@{
var content = GetPropertyCustomValue(item, column);
}
<button style="text-overflow:ellipsis;overflow:hidden;max-width:100px;max-height:50px;white-space:nowrap;" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="@content">
@content
</button>
</td>
}
else if (column.PropertyType == typeof(bool))
{
<td class="nowrap-col">
@if ((column.GetValue(item).ToString()) == "True")
{
<input type="checkbox" checked="checked" value="true" disabled="disabled" />
}
</td>
}
else
{
<td class="nowrap-col">@GetPropertyCustomValue(item, column)</td>
}
}
<td class="sticky-col-right">
<div class="btn-group">
<button @onclick="async () => await OnUpdate.InvokeAsync(item)" class="btn btn-sm btn-warning">ویرایش</button>
<button @onclick="async () => await OnDelete.InvokeAsync(item)" class="btn btn-sm btn-danger">حذف</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
<nav class="pagination-wrapper">
<ul class="pagination">
@foreach (var i in Enumerable.Range(1, TotalPages))
{
<li class="page-item @(CurrentPage == i ? "active" : "")">
<a class="page-link" @onclick="() => GoToPage(i)">@i</a>
</li>
}
</ul>
</nav>
</div>
}
@code {
[Parameter] public List<TItem> Data { get; set; } = new();
[Parameter] public List<PropertyInfo> Columns { get; set; } = new();
[Parameter] public int PageSize { get; set; } = 5;
//[Parameter] public List<(string, List<string>)> OtherValues { get; set; } = new();
[Parameter] public EventCallback<TItem> OnUpdate { get; set; }
[Parameter] public EventCallback<TItem> OnDelete { get; set; }
private int CurrentPage = 1;
private string SortColumn = "";
private bool SortAscending = true;
private string SearchText = "";
private IEnumerable<TItem> FilteredData => string.IsNullOrWhiteSpace(SearchText)
? Data
: Data.Where(item => Columns.Any(col => GetPropertyValue(item, col.Name)?.ToString()?.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ?? false));
private IEnumerable<TItem> SortedData =>
string.IsNullOrWhiteSpace(SortColumn)
? FilteredData
: (SortAscending
? FilteredData.OrderBy(item => GetPropertyValue(item, SortColumn))
: FilteredData.OrderByDescending(item => GetPropertyValue(item, SortColumn)));
private IEnumerable<TItem> PagedData => SortedData
.Skip((CurrentPage - 1) * PageSize)
.Take(PageSize);
private int TotalPages => (int)Math.Ceiling((double)SortedData.Count() / PageSize);
private void SortBy(string column)
{
if (SortColumn == column)
SortAscending = !SortAscending;
else
{
SortColumn = column;
SortAscending = true;
}
CurrentPage = 1;
}
private void GoToPage(int page)
{
CurrentPage = page;
StateHasChanged();
}
private object? GetPropertyValue(object obj, string propName)
{
var ss = obj.GetType().GetProperty(propName)?.GetValue(obj);
return obj.GetType().GetProperty(propName)?.GetValue(obj);
}
private object? GetPropertyCustomValue(object obj, PropertyInfo prop)
{
return prop.GetCustomValue(obj);
// var result = obj.GetType().GetProperty(prop.Name)?.GetValue(obj);
// var attr = prop.GetCustomAttribute<PriceAttriute>();
// if(attr != null)
// {
// result = ((long)result).ToString(attr.Format);
// }
// return result;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
}
await base.OnAfterRenderAsync(firstRender);
}
}
+94
View File
@@ -0,0 +1,94 @@
@* <div class="p-4 bg-white dark:bg-gray-900 rounded-lg shadow-md"> *@
<!-- Toolbar -->
@* <div class="flex gap-2 mb-2">
<button @onclick='() => ExecCmd(" bold")' class="btn">B</button>
<button @onclick='() => ExecCmd(" italic")' class="btn">I</button>
<button @onclick='() => ExecCmd(" underline")' class="btn">U</button>
<button @onclick="Clear" class="btn text-red-600 dark:text-red-400">پاک‌سازی</button>
</div> *@
<!-- Editable Content -->
<div @ref="EditorRef" id="@Id"
@oninput="UpdateContent"
contenteditable="true"
class="@Class">
</div>
<!-- Preview (Optional) -->
@* <div class="mt-4">
<h3 class="text-gray-600 dark:text-gray-400 mb-1 text-sm">پیش‌نمایش HTML:</h3>
<pre class="bg-gray-100 dark:bg-gray-800 text-xs p-2 rounded overflow-auto text-gray-700 dark:text-gray-300">@Content</pre>
</div> *@
@* </div> *@
@code {
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
[Parameter]
public string? Class { get; set; }
[Parameter]
public string? Id { get; set; }
private ElementReference EditorRef;
private string Content = "";
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if(!String.IsNullOrEmpty(Value))
{
Content = Value;
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
await JS.InvokeVoidAsync("setEditorContent", EditorRef, Content);
}
}
private async void ExecCmd(string command)
{
await JS.InvokeVoidAsync("document.execCommand", command, false, null);
await UpdateContent();
}
[JSInvokable]
private async Task UpdateContent()
{
Content = await JS.InvokeAsync<string>("getEditorContent", EditorRef);
StateHasChanged();
await ValueChanged.InvokeAsync(Content);
}
private async void Clear()
{
await JS.InvokeVoidAsync("clearEditor", EditorRef);
Content = "";
}
[Inject]
IJSRuntime JS { get; set; }
}
+26
View File
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Components;
namespace Mf.Razor.Core;
public class BaseForm : ComponentBase
{
[Parameter] public RenderFragment? ChildContent { get; set; }
protected bool IsLoading { get; set; } = true;
protected override async Task OnInitializedAsync()
{
IsLoading = true;
StateHasChanged();
await LoadDataAsync();
IsLoading = false;
StateHasChanged();
}
protected virtual Task LoadDataAsync()
{
IsLoading = false;
StateHasChanged();
return Task.CompletedTask;
}
}
-6
View File
@@ -1,6 +0,0 @@
namespace Mf.Razor.Interfaces
{
public interface IServiceLocator
{
}
}
+6 -6
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
@@ -12,12 +12,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.0"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.9"/>
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.15.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.15.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.9" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.17.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.17.0" />
</ItemGroup>
<ItemGroup>
+4 -1
View File
@@ -1,4 +1,5 @@
using Mf.Razor.Events;
using Mf.Razor.Entity.Interfaces;
using Mf.Razor.Events;
using Mf.Razor.Interfaces;
namespace Mf.Razor.Services
@@ -45,5 +46,7 @@ namespace Mf.Razor.Services
}
}
public string Token { get; }
}
}
+7 -1
View File
@@ -1 +1,7 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using Mf.Razor.Core
@using Mf.Core.Services
@using Mf.Core.SubEntities
@using Mf.Core.SubEntities.Auth
@using Mf.Razor.Entity.Services