升级wtm基础框架至6.1.0

This commit is contained in:
iioter 2022-08-23 13:47:00 +08:00
parent 30bd58b4aa
commit 4f5d897ac1
49 changed files with 995 additions and 485 deletions

View File

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -22,6 +22,7 @@
<script src="/jquery.cookie.js"></script>
<script src="/echarts/json-fns.js"></script>
<script src="/layui/layui.js"></script>
<script src="/layui/xm-select.js"></script>
<script src="/echarts/echarts.min.js"></script>
<script src="/echarts/chalk.js"></script>
<script src="/echarts/essos.js"></script>

File diff suppressed because one or more lines are too long

View File

@ -56,7 +56,11 @@ namespace WalkingTec.Mvvm.Core
}
set
{
_domains = value;
_domains = new Dictionary<string, Domain>();
foreach (var domain in value)
{
_domains.Add(domain.Key.ToLower(), domain.Value);
}
foreach (var item in _domains)
{
if(item.Value != null)

View File

@ -177,7 +177,8 @@ namespace WalkingTec.Mvvm.Core
var AllModules = allModules as List<SimpleModule>;
var roles = new FrameworkRole[]
{
new FrameworkRole{ ID = Guid.NewGuid(), RoleCode = "001", RoleName = CoreProgram._localizer?["Sys.Admin"]}
new FrameworkRole{ ID = Guid.NewGuid(), RoleCode = "001", CreateBy="Admin" , RoleName = CoreProgram._localizer?["Sys.Admin"]},
new FrameworkRole{ ID = Guid.NewGuid(), RoleCode = "002", RoleName = CoreProgram._localizer?["_Admin.User"]},
};
var adminRole = roles[0];
@ -1069,7 +1070,8 @@ namespace WalkingTec.Mvvm.Core
var AllModules = allModules as List<SimpleModule>;
var roles = new FrameworkRole[]
{
new FrameworkRole{ ID = Guid.NewGuid(), RoleCode = "001", RoleName = CoreProgram._localizer?["Sys.Admin"]}
new FrameworkRole{ ID = Guid.NewGuid(), RoleCode = "001", RoleName = CoreProgram._localizer?["Sys.Admin"]},
new FrameworkRole{ ID = Guid.NewGuid(), RoleCode = "002", RoleName = CoreProgram._localizer?["_Admin.User"]},
};
var adminRole = roles[0];

View File

@ -48,7 +48,8 @@ namespace WalkingTec.Mvvm.Core
public enum ButtonTypesEnum
{
Button,
Link
Link,
Img
};
/// <summary>

View File

@ -277,6 +277,8 @@ namespace WalkingTec.Mvvm.Core.Extensions
private static IQueryable<T> AppendSelfDPWhere<T>(IQueryable<T> query, WTMContext wtmcontext, List<SimpleDataPri> dps) where T : TopBasePoco
{
var dpsSetting = wtmcontext?.DataPrivilegeSettings;
Type modelTye = typeof(T);
bool isBasePoco = typeof(IBasePoco).IsAssignableFrom(modelTye);
ParameterExpression pe = Expression.Parameter(typeof(T));
Expression peid = Expression.Property(pe, typeof(T).GetSingleProperty("ID"));
//循环数据权限加入到where条件中达到自动过滤的效果
@ -293,7 +295,15 @@ namespace WalkingTec.Mvvm.Core.Extensions
var ids = dps.Where(x => x.TableName == query.ElementType.Name).Select(x => x.RelateId).ToList();
if (ids == null || ids.Count() == 0)
{
//if (isBasePoco == true)
//{
// var selfexp = Expression.Equal(Expression.Property(pe, "CreateBy"), Expression.Constant(wtmcontext.LoginUserInfo?.ITCode));
// query = query.Where(Expression.Lambda<Func<T, bool>>(selfexp, pe));
//}
//else
//{
query = query.Where(Expression.Lambda<Func<T, bool>>(Expression.NotEqual(Expression.Constant(1), Expression.Constant(1)), pe));
//}
}
else
{
@ -315,35 +325,43 @@ namespace WalkingTec.Mvvm.Core.Extensions
/// <param name="wtmcontext"></param>
/// <param name="IdFields">关联表外键</param>
/// <returns>修改后的查询语句</returns>
public static IQueryable<T> DPWhere<T>(this IQueryable<T> baseQuery, WTMContext wtmcontext, params Expression<Func<T, object>>[] IdFields) where T : TopBasePoco
{
var dps = wtmcontext?.LoginUserInfo?.DataPrivileges;
//循环所有关联外键
List<string> tableNameList = new List<string>();
foreach (var IdField in IdFields)
{
//public static IQueryable<T> DPWhere<T>(this IQueryable<T> baseQuery, WTMContext wtmcontext, params Expression<Func<T, object>>[] IdFields) where T : TopBasePoco
//{
// var dps = wtmcontext?.LoginUserInfo?.DataPrivileges;
// //循环所有关联外键
// List<string> tableNameList = new List<string>();
// foreach (var IdField in IdFields)
// {
//将外键 Id 用.分割循环生成指向最终id的表达式比如x=> x.a.b.Id
var fieldName = IdField.GetPropertyName(false);
//获取关联的类
string typename = "";
//如果外键名称不是id则根据model层的命名规则它应该是xxxId所以抹掉最后的 Id 应该是关联的类名
if (fieldName.ToLower() != "id")
{
fieldName = fieldName.Remove(fieldName.Length - 2);
typename = IdField.GetPropertyInfo().DeclaringType.GetSingleProperty(fieldName).PropertyType.Name;
}
//如果是 Id则本身就是关联的类
else
{
typename = typeof(T).Name;
}
tableNameList.Add(typename);
// //将外键 Id 用.分割循环生成指向最终id的表达式比如x=> x.a.b.Id
// var fieldName = IdField.GetPropertyName(false);
// //获取关联的类
// string typename = "";
// //如果外键名称不是id则根据model层的命名规则它应该是xxxId所以抹掉最后的 Id 应该是关联的类名
// if (fieldName.ToLower() != "id")
// {
// fieldName = fieldName.Remove(fieldName.Length - 2);
// var dtype = IdField.GetPropertyInfo().DeclaringType;
// if (dtype == typeof(TreePoco) && fieldName == "Parent")
// {
// typename = typeof(T).Name;
// }
// else
// {
// typename = dtype.GetSingleProperty(fieldName).PropertyType.Name;
// }
// }
// //如果是 Id则本身就是关联的类
// else
// {
// typename = typeof(T).Name;
// }
// tableNameList.Add(typename);
}
//var test = DPWhere(baseQuery, dps, tableNameList, IdFields);
return DPWhere(baseQuery, wtmcontext, tableNameList, IdFields);
}
// }
// //var test = DPWhere(baseQuery, dps, tableNameList, IdFields);
// return DPWhere(baseQuery, wtmcontext, tableNameList, IdFields);
//}
#region AddBy YOUKAI 20160310
/// <summary>
@ -352,10 +370,9 @@ namespace WalkingTec.Mvvm.Core.Extensions
/// <typeparam name="T">源数据类</typeparam>
/// <param name="baseQuery">源Query</param>
/// <param name="wtmcontext">wtm context</param>
/// <param name="tableName">关联数据权限的表名,如果关联外键为自身,则参数第一个为自身</param>
/// <param name="IdFields">关联表外键</param>
/// <returns>修改后的查询语句</returns>
public static IQueryable<T> DPWhere<T>(this IQueryable<T> baseQuery, WTMContext wtmcontext, List<string> tableName, params Expression<Func<T, object>>[] IdFields) where T : TopBasePoco
public static IQueryable<T> DPWhere<T>(this IQueryable<T> baseQuery, WTMContext wtmcontext, params Expression<Func<T, object>>[] IdFields) where T : TopBasePoco
{
var dps = wtmcontext?.LoginUserInfo?.DataPrivileges;
@ -370,79 +387,99 @@ namespace WalkingTec.Mvvm.Core.Extensions
//循环所有关联外键
foreach (var IdField in IdFields)
{
bool mtm = false;
Expression exp = trueExp;
//将外键Id用.分割循环生成指向最终id的表达式比如x=> x.a.b.Id
var fullname = IdField.GetPropertyName();
string[] splits = fullname.Split('.');
int leftindex = splits[0].IndexOf('[');
if (leftindex > 0)
{
mtm = true;
splits[0] = splits[0].Substring(0, leftindex);
}
Expression peid = Expression.MakeMemberAccess(pe, pe.Type.GetSingleProperty(splits[0]));
Type middletype = null;
if (mtm)
{
middletype = peid.Type.GetGenericArguments()[0];
List<(Expression exp, ParameterExpression pe, bool islist)> data = new System.Collections.Generic.List<(Expression, ParameterExpression, bool)>();
Expression iexp = pe;
ParameterExpression ipe = pe;
//格式化idfeild保存在data中
for (int i=0;i<splits.Length;i++)
{
var item = splits[i];
var proname = item;
int lindex = proname.IndexOf('[');
bool islist = false;
if (lindex > 0)
{
islist = true;
proname = proname.Substring(0, lindex);
}
iexp = Expression.MakeMemberAccess(iexp, iexp.Type.GetSingleProperty(proname));
Type petype = null;
if (islist == true)
{
petype = iexp.Type.GetGenericArguments()[0];
}
else
{
for (int i = 1; i < splits.Length; i++)
petype = iexp.Type;
}
if (petype == typeof(TreePoco))
{
peid = Expression.MakeMemberAccess(peid, peid.Type.GetSingleProperty(splits[i]));
petype = typeof(T);
}
middletype = (peid as MemberExpression).Member.DeclaringType;
if(islist == true || i == splits.Length-1)
{
data.Add((iexp, ipe, islist));
ipe = Expression.Parameter(petype);
iexp = ipe;
}
}
//确定最终关联的表名
string tableName = "";
if(data.Count > 0)
{
var last = data.Last().exp as MemberExpression;
string fieldname = last?.Member?.Name;
if (string.IsNullOrEmpty(fieldname) == false)
{
if (fieldname.ToLower() == "id")
{
tableName = last.Member.ReflectedType.Name;
}
else
{
var pro2 = wtmcontext.DC.GetPropertyNameByFk(last.Member.ReflectedType, fieldname);
if (string.IsNullOrEmpty(pro2) == false)
{
tableName = last.Member.ReflectedType.GetSingleProperty(pro2).PropertyType.Name;
}
}
}
}
//如果dps为空则拼接一个返回假的表达式这样就查询不出任何数据
if (dps == null)
if (dps == null || tableName == "")
{
exp = falseExp;
}
else
{
var fieldName = IdField.GetPropertyName(false);
//如果外键名称不是id则根据model层的命名规则它应该是xxxId所以抹掉最后的 Id 应该是关联的类名
if (fieldName.ToLower() != "id")
{
fieldName = fieldName.Remove(fieldName.Length - 2);
var typeinfo = middletype.GetSingleProperty(fieldName);
//var IsTableName = tableName?.Where(x => x == fieldName).FirstOrDefault();
var IsTableName = tableName?.Where(x => x.ToLower() == typeinfo.PropertyType.Name.ToLower()).FirstOrDefault();
if (string.IsNullOrEmpty(IsTableName))
{
continue;
}
fieldName = IsTableName;
//typename = PropertyHelper.GetPropertyInfo(IdField).DeclaringType.GetProperty(fieldName).PropertyType.Name;
}
//如果是Id则本身就是关联的类
else
{
fieldName = tableName[tindex];
}
var dpsSetting = wtmcontext.DataPrivilegeSettings;
//循环系统设定的数据权限,如果没有和关联类一样的表,则跳过
if (dpsSetting.Where(x => x.ModelName == fieldName).SingleOrDefault() == null)
if (dpsSetting.Where(x => x.ModelName == tableName).FirstOrDefault() == null)
{
continue;
}
//获取dps中关联到关联类的id列表
var ids = dps.Where(x => x.TableName == fieldName).Select(x => x.RelateId).ToList();
var ids = dps.Where(x => x.TableName == tableName).Select(x => x.RelateId).ToList();
//如果没有关联的id则拼接一个返回假的where是语句查询不到任何数据
if (ids == null || ids.Count() == 0)
if (ids == null || ids.Count == 0)
{
exp = falseExp;
//if (peid.Type == typeof(Guid))
//bool isBasePoco = typeof(IBasePoco).IsAssignableFrom(data.Last().pe.Type);
//if (isBasePoco)
//{
// exp = Expression.Equal(peid, Expression.Constant(Guid.NewGuid()));
// exp = Expression.Equal(Expression.Property(data.Last().pe, "CreateBy"), Expression.Constant(wtmcontext.LoginUserInfo?.ITCode));
//}
//else
//{
// exp = Expression.Equal(peid, Expression.Constant(null));
exp = falseExp;
//}
}
//如果有关联 Id
@ -452,30 +489,30 @@ namespace WalkingTec.Mvvm.Core.Extensions
//如果关联 Id 包括null则代表可以访问所有数据就不需要再拼接where条件了
if (!ids.Contains(null))
{
if (mtm == true)
for(int i=data.Count-1; i>=0; i--)
{
ParameterExpression midpe = Expression.Parameter(middletype);
Expression middleid = Expression.PropertyOrField(midpe, IdField.GetPropertyName(false));
var d = data[i];
if(d.islist == true)
{
var lastd = data[i + 1];
var queryable = Expression.Call(
typeof(Queryable),
"AsQueryable",
new Type[] { middletype },
peid);
List<Guid> ddd = new List<Guid>();
new Type[] { lastd.pe.Type },
d.exp);
exp = Expression.Call(
typeof(Queryable),
"Any",
new Type[] { middletype },
new Type[] { lastd.pe.Type },
queryable,
Expression.Lambda(typeof(Func<,>).MakeGenericType(middletype, typeof(bool)), ids.GetContainIdExpression(middletype, midpe, middleid).Body, new ParameterExpression[] { midpe }));
Expression.Lambda(typeof(Func<,>).MakeGenericType(lastd.pe.Type, typeof(bool)), exp, new ParameterExpression[] { lastd.pe }));
}
else
{
exp = ids.GetContainIdExpression(typeof(T), pe, peid).Body;
exp = ids.GetContainIdExpression(d.pe.Type, d.pe, d.exp).Body;
}
}
}
}

View File

@ -355,10 +355,28 @@ namespace WalkingTec.Mvvm.Core.Extensions
/// <param name="self">a listvm</param>
/// <param name="PlainText">true to return plain text, false to return formated html, such as checkbox,buttons ...</param>
/// <param name="enumToString">use enum display name</param>
/// <param name="func">无参有返回委托,返回一个字典</param>
/// <returns>json string</returns>
public static string GetJson<T>(this IBasePagedListVM<T, BaseSearcher> self, bool PlainText = true, bool enumToString = true) where T : TopBasePoco, new()
public static string GetJson<T>(this IBasePagedListVM<T, BaseSearcher> self, bool PlainText = true, bool enumToString = true, Func<Dictionary<string, object>> func = null) where T : TopBasePoco, new()
{
return $@"{{""Data"":{self.GetDataJson(PlainText,enumToString)},""Count"":{self.Searcher.Count},""Page"":{self.Searcher.Page},""PageCount"":{self.Searcher.PageCount},""Msg"":""success"",""Code"":200}}";
if (!self.IsSearched) self.DoSearch();
StringBuilder builder = new("{", capacity: 1024);
var dic = func?.Invoke();
// 如果用户的附加字典不为空,则添加用户自定义的信息
if (dic != null) foreach (var item in dic) builder.Append($"\"{item.Key}\":\"{item.Value}\",");
// 设置wtm必要的数据
builder
.Append($"\"Code\":200,")
.Append($"\"Count\":{self.Searcher.Count},")
.Append($"\"Data\":{self.GetDataJson(PlainText, enumToString)},")
.Append($"\"Msg\":\"success\",")
.Append($"\"Page\":{self.Searcher.Page},")
.Append($"\"PageCount\":{self.Searcher.PageCount}")
.Append('}');
return builder.ToString();
}
public static object GetJsonForApi<T>(this IBasePagedListVM<T, BaseSearcher> self, bool PlainText = true) where T : TopBasePoco, new()

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace WalkingTec.Mvvm.Core.Extensions
{
public static class RequestExtension
{
public static async Task<ApiResult<string>> RedirectCall(this HttpRequest request, WTMContext wtm, string domainname)
{
HttpMethodEnum method = Enum.Parse<HttpMethodEnum>(request.Method.ToString());
ApiResult<string> rv = null;
if (method == HttpMethodEnum.GET)
{
rv = await wtm.CallAPI("mainhost", request.Path.ToString());
}
else
{
if(request.HasFormContentType == true)
{
Dictionary<string, string> data = new Dictionary<string, string>();
foreach (var item in request.Form)
{
data.Add(item.Key, item.Value);
}
rv = await wtm.CallAPI<string>("mainhost", request.Path.ToString(), method, data);
}
else
{
HttpContent data = new StreamContent(request.Body);
rv = await wtm.CallAPI<string>("mainhost", request.Path.ToString(), method, data);
}
}
return rv;
}
}
}

View File

@ -106,7 +106,7 @@ namespace WalkingTec.Mvvm.Core.Extensions
var s = "";
if (Format == null)
{
s = item.ToString();
s = item?.ToString()??"";
}
else
{

View File

@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using WalkingTec.Mvvm.Core.Extensions;
using WalkingTec.Mvvm.Core.Support.FileHandlers;
using WalkingTec.Mvvm.Core.Support.Quartz;
namespace WalkingTec.Mvvm.Core
{
@ -53,6 +54,7 @@ namespace WalkingTec.Mvvm.Core
dc.Database.EnsureCreated();
}
WtmFileProvider.Init(WtmConfigs, gd);
services.TryAddSingleton<QuartzHostService>();
return services;
}

View File

@ -127,10 +127,15 @@ namespace WalkingTec.Mvvm.Core
{
me = le.Body as MemberExpression;
}
if (le.Body is UnaryExpression)
else if (le.Body is UnaryExpression)
{
me = (le.Body as UnaryExpression).Operand as MemberExpression;
}
else if(le.Body is MethodCallExpression mexp && mexp.Method.Name == "get_Item")
{
object index = 0;
me = mexp.Object as MemberExpression;
}
}
string rv = "";
if (me != null)

View File

@ -12,7 +12,9 @@ namespace WalkingTec.Mvvm.Core
[Display(Name = "_Admin.Exception")]
Exception,
[Display(Name = "_Admin.Debug")]
Debug
Debug,
[Display(Name = "_Admin.Job")]
Job
};
/// <summary>

View File

@ -15,7 +15,7 @@ namespace WalkingTec.Mvvm.Core
/// FrameworkUser
/// </summary>
[Table("FrameworkUsers")]
public class FrameworkUserBase : BasePoco
public abstract class FrameworkUserBase : BasePoco
{
[Display(Name = "_Admin.Account")]
[Required(ErrorMessage = "Validate.{0}required")]

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WalkingTec.Mvvm.Core.Support.Quartz
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class QuartzJobAttribute : Attribute
{
public string Name { get; set; }
public QuartzJobAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class QuartzGroupAttribute : Attribute
{
public string Group { get; set; }
public QuartzGroupAttribute(string group)
{
this.Group = group;
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class QuartzRepeatAttribute : Attribute
{
public int Repeat { get; set; }
public bool IsForever { get; set; }
public int IntervalInSeconds { get; set; }
/// <summary>
///
/// </summary>
/// <param name="interval">间隔时间,单位秒</param>
/// <param name="repeat">重复的次数</param>
/// <param name="isForever">是否一直重复</param>
public QuartzRepeatAttribute(int interval,int repeat, bool isForever)
{
this.Repeat = repeat;
this.IsForever = isForever;
this.IntervalInSeconds = interval;
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class QuartzStartAtAttribute : Attribute
{
public string Cron { get; set; }
/// <summary>
///
/// </summary>
/// <param name="cron">表示复杂格式的日期详见https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/crontrigger.html#introduction</param>
public QuartzStartAtAttribute(string cron)
{
this.Cron = cron;
}
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Quartz;
using Quartz.Impl;
namespace WalkingTec.Mvvm.Core.Support.Quartz
{
public class QuartzHostService : IHostedService
{
private readonly IServiceProvider _sp;
private readonly GlobalData _gd;
private IScheduler _scheduler;
public QuartzHostService(IServiceProvider sp,GlobalData gd)
{
_sp = sp;
_gd = gd;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
ISchedulerFactory factory = new StdSchedulerFactory();
_scheduler = await factory.GetScheduler();
foreach (var ass in _gd.AllAssembly)
{
try
{
var t = ass.GetExportedTypes().Where(x => typeof(WtmJob).IsAssignableFrom(x) && x.Name != "WtmJob").ToList();
int count = 1;
foreach (var st in t)
{
var ci = st.GetConstructor(Type.EmptyTypes);
var job = ci?.Invoke(null) as WtmJob;
if (job != null)
{
// job.Sp = _sp;
var attrs = st.GetCustomAttributes(true);
string jobName = st.Name;
string groupName = "group"+count;
var nameAttr = attrs.Where(x => x is QuartzJobAttribute).FirstOrDefault() as QuartzJobAttribute;
var groupAttr = attrs.Where(x => x is QuartzGroupAttribute).FirstOrDefault() as QuartzGroupAttribute;
var repeatAttr = attrs.Where(x => x is QuartzRepeatAttribute).FirstOrDefault() as QuartzRepeatAttribute;
var startAttr = attrs.Where(x => x is QuartzStartAtAttribute).FirstOrDefault() as QuartzStartAtAttribute;
if (nameAttr != null)
{
jobName = nameAttr.Name;
}
if(groupAttr != null)
{
groupName = groupAttr.Group;
}
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.Add("Sp", _sp);
// 创建作业
IJobDetail j = JobBuilder.Create(st)
.WithIdentity(jobName, groupName)
.UsingJobData(jobDataMap)
.Build();
// 创建触发器每60s执行一次
var builder = TriggerBuilder.Create()
.WithIdentity("trigger" + count, "group1");
if (startAttr != null)
{
builder = builder.WithCronSchedule(startAttr.Cron);
}
else {
builder = builder.StartNow();
if(repeatAttr != null)
{
if(repeatAttr.IsForever == true)
{
builder = builder.WithSimpleSchedule(x => x.WithIntervalInSeconds(repeatAttr.IntervalInSeconds).RepeatForever());
}
else
{
builder = builder.WithSimpleSchedule(x => x.WithRepeatCount(repeatAttr.Repeat).WithIntervalInSeconds(repeatAttr.IntervalInSeconds));
}
}
}
var trigger = builder.Build();
await _scheduler.ScheduleJob(j, trigger);
count++;
}
}
}
catch { }
}
// 开始运行
await Task.Delay(5000);
await _scheduler.Start(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await _scheduler.Shutdown(cancellationToken);
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
namespace WalkingTec.Mvvm.Core.Support.Quartz
{
public class WtmJob : IJob,IDisposable
{
private IServiceScope _ss;
private WTMContext _wtm;
protected WTMContext Wtm
{
get
{
if(_wtm == null)
{
_ss = Sp.CreateScope();
_wtm = _ss.ServiceProvider.GetRequiredService<WTMContext>();
}
return _wtm;
}
}
public IServiceProvider Sp { get; set; }
public virtual async Task Execute(IJobExecutionContext context)
{
await Task.Run(() => { });
}
public void Dispose()
{
_ss.Dispose();
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;
using Quartz.Spi;
namespace WalkingTec.Mvvm.Core.Support.Quartz
{
public class WtmJobFactory : IJobFactory
{
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var rv = Activator.CreateInstance(bundle.JobDetail.GetType()) as IJob;
return rv;
}
public void ReturnJob(IJob job)
{
throw new NotImplementedException();
}
}
}

View File

@ -55,7 +55,7 @@ namespace WalkingTec.Mvvm.Core
public bool IsEnabled(LogLevel logLevel)
{
if(logConfig == null)
if(logConfig == null || categoryName == "VueCliMiddleware")
{
return false;
}

View File

@ -231,21 +231,21 @@ namespace WalkingTec.Mvvm.Core
return null;
}
var code = await BaseUserQuery.Where(x => x.ITCode.ToLower() == itcode.ToLower()).Select(x =>new { itcode = x.ITCode, id= x.GetID(), photoid=x.PhotoId, name=x.Name }).SingleOrDefaultAsync();
if (code == null)
var password = await BaseUserQuery.Where(x => x.ITCode.ToLower() == itcode.ToLower()).Select(x =>x.Password).SingleOrDefaultAsync();
if (this.HttpContext.Request.Headers.ContainsKey("Authorization"))
{
return null;
var user = await CallAPI<LoginUserInfo>("", GetServerUrl() + "/api/_account/loginjwt", HttpMethodEnum.POST, new { Account = itcode, Password = password, IsReload=true });
return user?.Data;
}
LoginUserInfo rv = new LoginUserInfo
else
{
ITCode = code.itcode,
UserId = code.id?.ToString(),
Name = code.name,
PhotoId = code.photoid
};
await rv.LoadBasicInfoAsync(this);
return rv;
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("account", itcode);
data.Add("password", password);
data.Add("withmenu", "false");
var user = await CallAPI<LoginUserInfo>("", this.HttpContext.Request.Scheme + "://" + this.HttpContext.Request.Host.ToString() + "/api/_account/login", HttpMethodEnum.POST, data);
return user?.Data;
}
}
#endregion
@ -459,7 +459,7 @@ namespace WalkingTec.Mvvm.Core
return isPublic;
}
public void DoLog(string msg, ActionLogTypesEnum logtype = ActionLogTypesEnum.Normal)
public void DoLog(string msg, ActionLogTypesEnum logtype = ActionLogTypesEnum.Normal,string moduleName="", string actionName="", string ip="",string url = "", double duration = 0)
{
var log = this.Log?.GetActionLog();
if (log == null)
@ -469,6 +469,15 @@ namespace WalkingTec.Mvvm.Core
log.LogType = logtype;
log.ActionTime = DateTime.Now;
log.Remark = msg;
log.ActionUrl = url;
log.Duration = duration;
log.ModuleName = moduleName;
log.ActionName = actionName;
log.IP = ip;
if(string.IsNullOrEmpty(url) && this.HttpContext?.Request != null)
{
log.ActionUrl = this.HttpContext.Request.Path.ToString();
}
LogLevel ll = LogLevel.Information;
switch (logtype)
{
@ -960,6 +969,24 @@ namespace WalkingTec.Mvvm.Core
return await CallAPI<string>(domainName, url, method, postdata, timeout, proxy);
}
private string GetServerUrl()
{
var server = ConfigInfo.Domains.Where(x => x.Key.ToLower() == "serverpub").Select(x => x.Value).FirstOrDefault();
if (server == null)
{
server = ConfigInfo.Domains.Where(x => x.Key.ToLower() == "server").Select(x => x.Value).FirstOrDefault();
}
if (server != null && string.IsNullOrEmpty(server.Address) == false)
{
return server.Address.TrimEnd('/');
}
else
{
return this.HttpContext.Request.Scheme + "://" + this.HttpContext.Request.Host.ToString();
}
}
#endregion
}

View File

@ -7,21 +7,20 @@
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
<PackageReference Include="DotNetCore.NPOI" Version="1.2.3" />
<PackageReference Include="Fare" Version="2.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.DataAnnotations.Validation" Version="3.2.0-rc1.20223.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
@ -29,16 +28,17 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.2" />
<PackageReference Include="Oracle.EntityFrameworkCore" Version="6.21.5" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.1" />
<PackageReference Include="Oracle.EntityFrameworkCore" Version="6.21.4" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0" />
<PackageReference Include="Quartz" Version="3.4.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.3" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
</ItemGroup>

View File

@ -121,11 +121,6 @@ namespace WalkingTec.Mvvm.Mvc.Auth
TenantCode = pair[1],
};
// 清理过期 refreshtoken
var sql = $"DELETE FROM {DC.GetTableName<PersistedGrant>()} WHERE Expiration<'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}'";
_dc.RunSQL(sql);
_logger.LogDebug("清理过期的refreshToken【sql:{0}】", sql);
// 颁发 token
return await IssueTokenAsync(loginUserInfo);
}

View File

@ -2159,9 +2159,16 @@ namespace WalkingTec.Mvvm.Mvc
string template = "";
string newname = item.FieldName;
if (mpro.PropertyType.IsBoolOrNullableBool())
{
if (mpro.PropertyType.IsNullable())
{
render = "ComponentType=\"@typeof(NullSwitch)\"";
}
else
{
render = "ComponentType=\"@typeof(Switch)\"";
}
}
if (mpro.PropertyType == typeof(DateTime) || mpro.PropertyType == typeof(DateTime?))
{
render = "FormatString=\"yyyy-MM-dd HH: mm: ss\"";
@ -2416,9 +2423,16 @@ namespace WalkingTec.Mvvm.Mvc
checktype = proType.GetGenericArguments()[0];
}
if (checktype == typeof(bool))
{
if (proType.IsNullable())
{
controltype = "NullSwitch";
}
else
{
controltype = "Switch";
}
}
else if (checktype.IsEnum())
{
controltype = "Select";
@ -2542,8 +2556,15 @@ namespace WalkingTec.Mvvm.Mvc
checktype = proType.GetGenericArguments()[0];
}
if (checktype == typeof(bool))
{
if (proType.IsNullable())
{
controltype = "NullSwitch";
}
else
{
controltype = "Switch";
}
disabled = "IsDisabled=\"true\"";
}
}

View File

@ -43,7 +43,6 @@ namespace WalkingTec.Mvvm.Mvc.Filters
var postDes = ctrlActDesc.MethodInfo.GetCustomAttributes(typeof(HttpPostAttribute), false).Cast<HttpPostAttribute>().FirstOrDefault();
var validpostonly = ctrlActDesc.MethodInfo.GetCustomAttributes(typeof(ValidateFormItemOnlyAttribute), false).Cast<ValidateFormItemOnlyAttribute>().FirstOrDefault();
log.ITCode = ctrl.Wtm.LoginUserInfo?.ITCode ?? string.Empty;
//给日志的多语言属性赋值
log.ModuleName = ctrlDes?.GetDescription(ctrl) ?? ctrlActDesc.ControllerName;
log.ActionName = actDes?.GetDescription(ctrl) ?? ctrlActDesc.ActionName + (postDes == null ? string.Empty : "[P]");

View File

@ -44,11 +44,11 @@ $columns$
<div style="padding-right:10px;">
@if (IsAccessable("/api/$modelname$/Edit"))
{
<TableCellButton Size="Size.ExtraSmall" Color="Color.Success" Icon="fa fa-edit" Text="@WtmBlazor.Localizer["Sys.Edit"]" OnClickCallback="() => OnEditClick(context)" />
<TableCellButton Size="Size.ExtraSmall" Color="Color.Success" Icon="fa fa-edit" Text="@WtmBlazor.Localizer["Sys.Edit"]" OnClick="() => OnEditClick(context)" />
}
@if (IsAccessable("/api/$modelname$/{id}"))
{
<TableCellButton Size="Size.ExtraSmall" Color="Color.Info" Icon="fa fa-info" Text="@WtmBlazor.Localizer["Sys.Details"]" OnClickCallback="()=>OnDetailsClick(context)" />
<TableCellButton Size="Size.ExtraSmall" Color="Color.Info" Icon="fa fa-info" Text="@WtmBlazor.Localizer["Sys.Details"]" OnClick="()=>OnDetailsClick(context)" />
}
@if (IsAccessable("/api/$modelname$/BatchDelete"))
{

View File

@ -45,6 +45,7 @@ using WalkingTec.Mvvm.Mvc.Helper;
using WalkingTec.Mvvm.TagHelpers.LayUI;
using Microsoft.AspNetCore.SpaServices.Extensions;
using Microsoft.Extensions.FileProviders;
using WalkingTec.Mvvm.Core.Support.Quartz;
namespace WalkingTec.Mvvm.Mvc
{
@ -71,7 +72,7 @@ namespace WalkingTec.Mvvm.Mvc
private static GlobalData GetGlobalData()
{
var gd = new GlobalData();
gd.AllAssembly = Utils.GetAllAssembly();
return gd;
}
@ -502,6 +503,7 @@ namespace WalkingTec.Mvvm.Mvc
y.ValueLengthLimit = int.MaxValue - 20480;
y.MultipartBodyLengthLimit = conf.FileUploadOptions.UploadLimit;
});
services.AddHostedService<QuartzHostService>();
return services;
}
public static IServiceCollection AddWtmCrossDomain(this IServiceCollection services, IConfiguration config)
@ -553,7 +555,7 @@ namespace WalkingTec.Mvvm.Mvc
public static IServiceCollection AddWtmAuthentication(this IServiceCollection services, IConfiguration config)
{
var conf = config.Get<Configs>();
services.AddSingleton<ITokenService, TokenService>();
services.AddScoped<ITokenService, TokenService>();
var jwtOptions = conf.JwtOptions;
@ -698,7 +700,6 @@ namespace WalkingTec.Mvvm.Mvc
var localfactory = app.ApplicationServices.GetRequiredService<IStringLocalizerFactory>();
var lop = app.ApplicationServices.GetService<WtmLocalizationOption>();
//获取所有程序集
gd.AllAssembly = Utils.GetAllAssembly();
//var mvc = GetRuntimeAssembly("WalkingTec.Mvvm.Mvc");
//if (mvc != null && gd.AllAssembly.Contains(mvc) == false)
//{

View File

@ -43,7 +43,8 @@ namespace WalkingTec.Mvvm.Mvc
_iconFontItems = iconFontHashSet.Select(x => new ComboSelectListItem
{
Text = x,
Value = x
Value = x,
Icon = x
}).ToList();
_iconFontDicItems = new Dictionary<string, List<MenuItem>>();

View File

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using Quartz;
using WalkingTec.Mvvm.Core;
using WalkingTec.Mvvm.Core.Extensions;
using WalkingTec.Mvvm.Core.Support.Quartz;
namespace WalkingTec.Mvvm.Mvc.Helper
{
[QuartzRepeat(3600,0,true)]
public class JwtRefreshJob : WtmJob
{
public override Task Execute(IJobExecutionContext context)
{
try
{
var sql = $"DELETE FROM {this.Wtm.DC.GetTableName<PersistedGrant>()} WHERE Expiration<'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}'";
this.Wtm.DC.RunSQL(sql);
Wtm.DoLog("清理过期的refreshToken", ActionLogTypesEnum.Job);
}
catch { }
return Task.CompletedTask;
}
}
}

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Options;
using WalkingTec.Mvvm.Core;
using WalkingTec.Mvvm.Core.Extensions;
namespace WalkingTec.Mvvm.Mvc
{
@ -20,17 +21,17 @@ namespace WalkingTec.Mvvm.Mvc
_next = next;
}
public async Task InvokeAsync(HttpContext context, IOptionsMonitor<Configs> configs)
public async Task InvokeAsync(HttpContext context, WTMContext wtm)
{
var max = context.Features.Get<IHttpMaxRequestBodySizeFeature>();
if (max.IsReadOnly == false)
{
max.MaxRequestBodySize = configs.CurrentValue.FileUploadOptions.UploadLimit;
max.MaxRequestBodySize = wtm.ConfigInfo.FileUploadOptions.UploadLimit;
}
if (context.Request.Path == "/")
{
context.Response.Cookies.Append("pagemode", configs.CurrentValue.PageMode.ToString());
context.Response.Cookies.Append("tabmode", configs.CurrentValue.TabMode.ToString());
context.Response.Cookies.Append("pagemode", wtm.ConfigInfo.PageMode.ToString());
context.Response.Cookies.Append("tabmode", wtm.ConfigInfo.TabMode.ToString());
}
if (context.Request.ContentLength > 0 && context.Request.ContentLength < 512000)
{
@ -48,6 +49,17 @@ namespace WalkingTec.Mvvm.Mvc
context.Items["DONOTUSE_REQUESTBODY"] = body;
}
}
//if(wtm.ConfigInfo.Domains != null)
//{
// var mainHost = wtm.ConfigInfo.Domains.Where(x=>x.Key== "mainhost").Select(x=>x.Value.Address).FirstOrDefault();
// if(string.IsNullOrEmpty(mainHost) == false)
// {
// if(context.Request.RouteValues["controller"]?.ToString()?.ToLower() == "login")
// {
// var test = await context.Request.RedirectCall(wtm, "mainhost");
// }
// }
//}
await _next(context);
if (context.Response.StatusCode == 404)
{

View File

@ -14,6 +14,7 @@ Layout = null;
<script src="/jquery.min.js"></script>
<script src="/jquery.cookie.js"></script>
<script src="/layui/layui.js"></script>
<script src="/layui/xm-select.js"></script>
<script src="/_js/framework_layui.js?time=@DateTime.Now.Ticks"></script>
<script>
var DONOTUSE_IGNOREHASH = false;

View File

@ -18,6 +18,7 @@
<script src="/jquery.min.js"></script>
<script src="/jquery.cookie.js"></script>
<script src="/layui/layui.js"></script>
<script src="/layui/xm-select.js"></script>
<script src="/_js/framework_layui.js?time=@DateTime.Now.Ticks"></script>
<script>
var DONOTUSE_IGNOREHASH = false;

View File

@ -15,6 +15,7 @@ Layout = null;
<script src="/jquery.min.js"></script>
<script src="/jquery.cookie.js"></script>
<script src="/layui/layui.js"></script>
<script src="/layui/xm-select.js"></script>
<script src="/_js/framework_layui.js?time=@DateTime.Now.Ticks"></script>
<script>
var DONOTUSE_IGNOREHASH = false;

View File

@ -10,7 +10,6 @@
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="GeneratorFiles\Spa\Vue\**\*.txt" />
<EmbeddedResource Include="Views\**\*.cshtml" />
@ -59,12 +58,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta13" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="VueCliMiddleware" Version="6.0.0" />
</ItemGroup>

View File

@ -35,6 +35,7 @@ namespace WalkingTec.Mvvm.Mvc
{
[HttpPost]
[Public]
public IActionResult Selector(string _DONOT_USE_VMNAME
, string _DONOT_USE_KFIELD
, string _DONOT_USE_VFIELD
@ -298,7 +299,7 @@ namespace WalkingTec.Mvvm.Mvc
{
var FileData = Request.Form.Files[0];
var file = fp.Upload(FileData.FileName, FileData.Length, FileData.OpenReadStream(), groupName, subdir, extra, sm, Wtm.CreateDC(cskey: _DONOT_USE_CS));
return JsonMore(new { Id = file.GetID(), Name = file.FileName});
return JsonMore(new { Id = file.GetID(), Name = file.FileName });
}
[HttpPost]
@ -329,7 +330,7 @@ namespace WalkingTec.Mvvm.Mvc
oimage.SaveAsJpeg(ms);
ms.Position = 0;
var file = fp.Upload(FileData.FileName, ms.Length, ms, groupName,subdir,extra,sm, Wtm.CreateDC(cskey: _DONOT_USE_CS));
var file = fp.Upload(FileData.FileName, ms.Length, ms, groupName, subdir, extra, sm, Wtm.CreateDC(cskey: _DONOT_USE_CS));
oimage.Dispose();
ms.Dispose();
return JsonMore(new { Id = file.GetID(), Name = file.FileName });
@ -423,7 +424,7 @@ namespace WalkingTec.Mvvm.Mvc
}
else
{
Response.Headers.TryAdd("Content-Disposition",$"inline; filename=\"{HttpUtility.UrlEncode( file.FileName)}\"");
Response.Headers.TryAdd("Content-Disposition", $"inline; filename=\"{HttpUtility.UrlEncode(file.FileName)}\"");
await rv.CopyToAsync(Response.Body);
rv.Dispose();
return new EmptyResult();
@ -455,6 +456,7 @@ namespace WalkingTec.Mvvm.Mvc
}
[Public]
public IActionResult OutSide(string url)
{
url = HttpUtility.UrlDecode(url);
@ -720,7 +722,7 @@ namespace WalkingTec.Mvvm.Mvc
{
int codeW = 80;
int codeH = 30;
int fontSize = 20;
int fontSize = 16;
string chkCode = string.Empty;
Color[] color = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.DarkBlue, Color.PaleGreen };
string[] font = { "Times New Roman" };
@ -751,8 +753,7 @@ namespace WalkingTec.Mvvm.Mvc
//画验证码
for (int i = 0; i < chkCode.Length; i++)
{
string fnt = font[rnd.Next(font.Length)];
Font ft = new Font(SystemFonts.Find(fnt), fontSize);
Font ft = new Font(SystemFonts.Families.First(), fontSize);
Color clr = color[rnd.Next(color.Length)];
bmp.Mutate(x => x.DrawText(chkCode[i].ToString(),ft,clr,new PointF((float)i * 18, (float)0)));
}
@ -854,7 +855,7 @@ namespace WalkingTec.Mvvm.Mvc
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return Content($"<script>window.location.href='{HttpUtility.UrlDecode(redirect)}';</script>","text/html");
return Content($"<script>window.location.href='{HttpUtility.UrlDecode(redirect)}';</script>", "text/html");
}

View File

@ -251,68 +251,10 @@ window.ff = {
},
RenderForm: function (formId) {
var comboxs = $(".layui-form[lay-filter=" + formId + "] select[wtm-combo='MULTI_COMBO']");
if (comboxs.length === 0) {
var comboxs = $(".layui-form[lay-filter=" + formId + "] div[wtm-ctype='combo']");
layui.use(['form'], function () {
var form = layui.form.render(null, formId);
});
}
else {
layui.use(['form', 'formSelects'], function () {
var formSelects = layui.formSelects;
layui.form.render(null, formId);
/* 启用 ComboBox 多选 */
for (var i = 0; i < comboxs.length; i++) {
var filter = comboxs[i].attributes['lay-filter'].value;
var vs = comboxs[i].attributes['wtm-combovalue'].value;
var vn = comboxs[i].attributes['wtm-comboname'].value;
var arr = [];
if (vs !== null && vs != "") {
var values = vs.split("`");
var names = vn.split("`");
for (var a = 0; a < values.length; a++) {
arr.push({ name: names[a], val: values[a] });
}
}
var changefunc = "1==1";
var chainchange = "";
var linkto = false;
var url = "";
var targetname = "";
var changefuncattr = comboxs[i].attributes['wtm-cf'];
var linktoattr = comboxs[i].attributes['wtm-linkto'];
var urlattr = comboxs[i].attributes['wtm-turl'];
if (changefuncattr != undefined) {
changefunc = changefuncattr.value + "(a)";
}
if (urlattr != undefined) {
url = urlattr.value;
}
if (linktoattr != undefined) {
linkto = true;
}
formSelects.on({
layFilter: filter, left: '', right: '', separator: ',', arr: arr,
url: url, self: comboxs[i], linkto: linkto, cf: changefunc,
selectFunc: function (a) {
try {
if (eval(this.cf) && this.linkto == true) {
var u = this.url;
if (u.indexOf("?") == -1) {
u += "?t=" + new Date().getTime();
}
for (var i = 0; i < a.length; i++) {
u += "&id=" + a[i].val;
}
ff.ChainChange(u, this.self)
}
}
catch (e) { }
}
});
}
});
}
},
PostForm: function (url, formId, divid, searchervm) {
@ -467,6 +409,11 @@ window.ff = {
, btn: []
, id: windowid //设定一个id防止重复弹出
, content: str
//, success: function (layero, index) {
// if (height == undefined || height == null || height == '' || max == false){
// document.getElementById('layui-layer' + index).getElementsByClassName('layui-layer-content')[0].style.overflow = 'unset';
// }
//}
, resizing: function (layero) {
ff.triggerResize();
$(layero).find("div[ischart = '1']").each(
@ -668,6 +615,8 @@ window.ff = {
var targetname = target.attr("wtm-name");
var ismulticombo = target.attr("wtm-combo") != undefined;
var targetid = target.attr("id");
var comboid = targetid;
if (controltype == undefined) {
controltype = "";
}
@ -678,8 +627,7 @@ window.ff = {
//clear
switch (controltype) {
case "combo":
target.html('<option value = "" selected>' + ff.DONOTUSE_Text_PleaseSelect + '</option>');
form.render('select', targetfilter);
window[comboid].update({ data: [] });
break;
case "checkbox":
target.html('');
@ -690,9 +638,7 @@ window.ff = {
form.render('radio', targetfilter);
break;
case "tree":
layui.tree.reload(targetid, {
data: []
});
window[comboid].update({ data: [] });
break;
case "transfer":
layui.transfer.reload(targetid, {
@ -707,9 +653,7 @@ window.ff = {
var item = null;
if (controltype === "tree") {
layui.tree.reload(targetid, {
data: ff.getTreeItems(data.Data)
});
window[comboid].update({ data: ff.getTreeItems(data.Data) });
}
if (controltype === "transfer") {
layui.transfer.reload(targetid, {
@ -718,26 +662,7 @@ window.ff = {
}
if (controltype === "combo") {
target.html('<option value = "" selected>' + ff.DONOTUSE_Text_PleaseSelect + '</option>');
var arr = [];
if (data.Data !== undefined && data.Data !== null) {
for (i = 0; i < data.Data.length; i++) {
item = data.Data[i];
var icon = item.Icon !== undefined && item.Icon != null && item.Icon.length > 0 ? ' icon="' + item.Icon + '"' : '';
if (item.Selected === true) {
target.append('<option value = "' + item.Value + '"' + icon + ' selected>' + item.Text + '</option>');
}
else {
target.append('<option value = "' + item.Value + '" ' + icon + '>' + item.Text + '</option>');
}
arr.push({ name: item.Text, val: item.Value });
}
}
form.render('select', targetfilter);
if (ismulticombo) {
var mm = layui.formSelects.selects[target.attr("lay-filter")];
ff.refreshcombobox(mm, []);
}
window[comboid].update({ data: ff.getComboItems(data.Data) });
}
if (controltype === "checkbox") {
for (i = 0; i < data.Data.length; i++) {
@ -787,9 +712,7 @@ window.ff = {
var item = null;
if (controltype === "tree") {
var da = ff.getTreeItems(data.Data, svals);
layui.tree.reload(controlid, {
data: da
});
window[controlid].update({ data: da });
if (cb !== undefined && cb != null) {
cb();
}
@ -800,29 +723,8 @@ window.ff = {
});
}
if (controltype === "combo") {
target.html('<option value = "" selected>' + ff.DONOTUSE_Text_PleaseSelect + '</option>');
var arr = [];
if (data.Data !== undefined && data.Data !== null) {
for (i = 0; i < data.Data.length; i++) {
item = data.Data[i];
var icon = item.Icon !== undefined && item.Icon != null && item.Icon.length > 0 ? ' icon="' + item.Icon + '"' : '';
if (item.Selected === true || svals.indexOf(item.Value) > -1) {
target.append('<option value = "' + item.Value + '"' + icon + ' selected>' + item.Text + '</option>');
arr.push({ name: item.Text, val: item.Value });
}
else {
target.append('<option value = "' + item.Value + '" ' + icon + '>' + item.Text + '</option>');
}
}
}
layui.form.render('select', targetfilter+"div");
if (ismulticombo) {
layui.use(['form', 'formSelects'], function () {
var mm = layui.formSelects.selects[targetfilter];
ff.refreshcombobox(mm, arr);
});
}
var da = ff.getComboItems(data.Data, svals);
window[controlid].update({ data: da });
}
if (controltype === "checkbox") {
for (i = 0; i < data.Data.length; i++) {
@ -892,6 +794,19 @@ window.ff = {
}
}
var xselect = searchForm.find("div[wtm-ctype='tree'],div[wtm-ctype='combo']");
layui.each(xselect, function (_, item) {
var val = window[item.id].getValue('value');
if (val.length > 1) {
fieldElem = fieldElem.filter(function (index) {
return this.name != item.attributes["wtm-name"].value;
})
$.each(val, function (i, v) {
fieldElem.push({ name: item.attributes["wtm-name"].value, value: v });
});
}
});
var check = {};
layui.each(fieldElem, function (_, item) {
if (!item.name) return;
@ -905,9 +820,6 @@ window.ff = {
return;
}
var itemname = item.name;
if (/_WTMMultiCombo_(.*?)_(.*?)$/.test(itemname)) {
itemname = RegExp.$2;
}
if (/_DONOTUSE_(.*?)\[(\d?)\]\.(.*?)$/.test(itemname)) {
var name1 = RegExp.$1;
var number = RegExp.$2;
@ -957,7 +869,7 @@ window.ff = {
}
else {
filter[itemname] = item.value;
if (filterback.hasOwnProperty(itemname) == true) {
if (filterback.hasOwnProperty(itemname) == true && item.value != "") {
filterback[itemname] = undefined;
}
}
@ -966,6 +878,7 @@ window.ff = {
for (item in filterback) {
if (filterback[item] !== undefined) {
filter[item] = undefined;
filter[item + ".length"] = "0";
}
}
@ -1248,21 +1161,34 @@ window.ff = {
for (var i = 0; i < data.length; i++) {
var item = {};
item.id = data[i].Value;
item.title = data[i].Text;
item.href = data[i].Url;
item.spread = data[i].Expended;
item.checked = data[i].Selected || svals.indexOf(data[i].Value) > -1;
item.value = data[i].Value;
item.name = data[i].Text;
item.disabled = data[i].Disabled;
item.selected = data[i].Selected || svals.indexOf(data[i].Value) > -1;
item.icon = data[i].Icon;
if (data[i].Children != null && data[i].Children.length > 0) {
item.children = this.getTreeItems(data[i].Children, svals);
}
rv.push(item);
}
return rv;
},
for (var j = 0; j < item.children.length; j++) {
if (item.children[j].checked == true || item.children[j].spread == true) {
item.spread = true;
break;
}
getComboItems: function (data, svals) {
var rv = [];
if (svals == undefined || svals == null) {
svals = [];
}
for (var i = 0; i < data.length; i++) {
var item = {};
item.value = data[i].Value;
item.name = data[i].Text;
item.disabled = data[i].Disabled;
item.selected = data[i].Selected || svals.indexOf(data[i].Value) > -1;
item.icon = data[i].Icon;
if (data[i].Children != null && data[i].Children.length > 0) {
item.children = this.getTreeItems(data[i].Children, svals);
}
rv.push(item);
}
@ -1280,7 +1206,7 @@ window.ff = {
item.value = data[i].Value;
item.title = data[i].Text;
item.disabled = data[i].Disabled;
item.checked = data[i].Selected || svals.indexOf(data[i].Value) > -1;
//item.checked = data[i].Selected || svals.indexOf(data[i].Value) > -1;
rv.push(item);
}
return rv;

View File

@ -44,6 +44,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
public string ConfirmTxt { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (string.IsNullOrEmpty(Id))
@ -92,9 +93,17 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
}
}
else
{
if (string.IsNullOrEmpty(Icon) == false)
{
output.Attributes.SetAttribute("class", "shortcut");
output.Content.SetHtmlContent($"<div><i class=\"{Icon}\"></i></div>{Text ?? ""}");
}
else
{
output.Content.SetHtmlContent(Text ?? string.Empty);
}
}
string submitButtonUrl = "";
if (this is SubmitButtonTagHelper sbt)
{

View File

@ -95,38 +95,38 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
break;
}
if (item.LinkField != null || item.LinkId != null)
{
if (!string.IsNullOrEmpty(item.TriggerUrl))
{
output.PostElement.AppendHtml($@"
<script>
layui.use(['form'],function(){{
var form = layui.form;
form.on('select({output.Attributes["lay-filter"].Value})', function(data){{
{FormatFuncName(item.ChangeFunc)};
ff.ChainChange('{item.TriggerUrl}/'+data.value,data.elem)
ff.changeComboIcon(data);
}});
}})
</script>
");
}
}
else
{
output.PostElement.AppendHtml($@"
<script>
layui.use(['form'],function(){{
var form = layui.form;
form.on('select({output.Attributes["lay-filter"].Value})', function(data){{
{FormatFuncName(item.ChangeFunc)};
ff.changeComboIcon(data);
}});
}})
</script>
");
}
// if (item.LinkField != null || item.LinkId != null)
// {
// if (!string.IsNullOrEmpty(item.TriggerUrl))
// {
// output.PostElement.AppendHtml($@"
//<script>
//layui.use(['form'],function(){{
// var form = layui.form;
// form.on('select({output.Attributes["lay-filter"].Value})', function(data){{
// {FormatFuncName(item.ChangeFunc)};
// ff.ChainChange('{item.TriggerUrl}/'+data.value,data.elem)
// ff.changeComboIcon(data);
// }});
//}})
//</script>
//");
// }
// }
// else
// {
// output.PostElement.AppendHtml($@"
//<script>
//layui.use(['form'],function(){{
// var form = layui.form;
// form.on('select({output.Attributes["lay-filter"].Value})', function(data){{
// {FormatFuncName(item.ChangeFunc)};
// ff.changeComboIcon(data);
// }});
//}})
//</script>
//");
// }
break;
case CheckBoxTagHelper item:
if (string.IsNullOrEmpty(item.ChangeFunc) == false)

View File

@ -96,23 +96,32 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
if (!(this is DisplayTagHelper) && ((Field.Metadata.IsRequired && Field.Name.Contains("[-1]")==false) || Required == true))
{
requiredDot = "<font color='red'>*</font>";
if (!(this is UploadTagHelper || this is RadioTagHelper || this is CheckBoxTagHelper || this is MultiUploadTagHelper || this is ColorPickerTagHelper || this is TreeTagHelper || this is SliderTagHelper)) // 上传组件自定义验证
if (!(this is UploadTagHelper || this is RadioTagHelper || this is CheckBoxTagHelper || this is MultiUploadTagHelper || this is ColorPickerTagHelper || this is SliderTagHelper || this is TransferTagHelper)) // 上传组件自定义验证
{
//richtextbox不需要进行必填验证
if (output.Attributes["isrich"] == null)
{
if (this is ComboBoxTagHelper combo && combo.MultiSelect.Value == true)
//combobox和tree用xmselect控件的验证
if (this is ComboBoxTagHelper combo || this is TreeTagHelper)
{
output.Attributes.Add("lay-verify", "selectRequired");
var script = $@"
<script>
window['{this.Id}'].update({{
layVerify:'required',
layReqText:'{THProgram._localizer["Validate.{0}required", Field?.Metadata?.DisplayName ?? Field?.Metadata?.Name]}'
}});
</script>
";
output.PostElement.AppendHtml(script);
}
else
{
output.Attributes.Add("lay-verify", "required");
}
output.Attributes.Add("lay-reqText", $"{THProgram._localizer["Validate.{0}required", Field?.Metadata?.DisplayName ?? Field?.Metadata?.Name]}");
}
}
}
}
if (LabelText == null)
{
@ -146,7 +155,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
preHtml += $@"
<div {(this is DisplayTagHelper ? "style=\"margin-bottom:0px;\"" : "")} class=""layui-form-item layui-form"" lay-filter=""{layfilter}div"">
<label for=""{Id}"" class=""layui-form-label"" {(LabelWidth == null ? string.Empty : "style='width:" + LabelWidth + "px'")}>{lb}</label>
<label for=""{Id}"" class=""layui-form-label"" {(LabelWidth == null ? "style='min-height:21px;'" : "style='min-height:21px;width:" + LabelWidth + "px'")}>{lb}</label>
<div class=""{ (string.IsNullOrEmpty(PaddingText) ? "layui-input-block" : "layui-input-inline")}"" {(LabelWidth == null || string.IsNullOrEmpty(PaddingText)==false ? "" : "style='margin-left:" + (LabelWidth + 30) + "px'")}>
";
}

View File

@ -165,7 +165,38 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI.Common
public string MakeViewButton(ButtonTypesEnum buttonType, Guid fileID, string buttonText = null, int? width = null, int? height = null, string title = null, bool resizable = true, string _DONOT_USE_CS = "default", bool maxed = false, string buttonClass = null, string style = null)
{
return MakeDialogButton(buttonType, $"/_Framework/ViewFile/{fileID}?_DONOT_USE_CS={_DONOT_USE_CS}&width={width}", buttonText, width, height, title, null, true, resizable, maxed,buttonClass, style);
var buttonID = Guid.NewGuid().ToString();
var innerClick = "";
string windowid = Guid.NewGuid().ToString();
var url = $"/_Framework/GetFile/{fileID}?_DONOT_USE_CS={_DONOT_USE_CS}";
innerClick = $"layui.layer.photos({{photos: {{data: [{{src: '{url}'}}]}},anim: 5}});";
string funcname = $"x{buttonID.Replace("-", "")}click";
var click = $"<script>function {funcname}(){{{innerClick};return false;}}</script>";
string rv = "";
if (buttonType == ButtonTypesEnum.Link)
{
rv = $"<a id='{buttonID}' onclick='{funcname}()' style='{style ?? "color:blue;cursor:pointer"}' class='{buttonClass ?? ""}'>{buttonText}</a>";
}
if (buttonType == ButtonTypesEnum.Button)
{
rv = $"<a id='{buttonID}' onclick='{funcname}()' style='{style ?? ""}' class='layui-btn {(string.IsNullOrEmpty(buttonClass) ? "layui-btn-primary layui-btn-xs" : $"{buttonClass}")}'>{buttonText}</a>";
}
switch (buttonType)
{
case ButtonTypesEnum.Button:
rv = $"<a id='{buttonID}' onclick='{funcname}()' style='{style ?? ""}' class='layui-btn {(string.IsNullOrEmpty(buttonClass) ? "layui-btn-primary layui-btn-xs" : $"{buttonClass}")}'>{buttonText}</a>";
break;
case ButtonTypesEnum.Link:
rv = $"<a id='{buttonID}' onclick='{funcname}()' style='{style ?? "color:blue;cursor:pointer"}' class='{buttonClass ?? ""}'>{buttonText}</a>";
break;
case ButtonTypesEnum.Img:
rv = $"<img src='{url}&width={width??50}&height={height??50}' id='{buttonID}' onclick='{funcname}()' style='{style ?? "color:blue;cursor:pointer"}' class='{buttonClass ?? ""}'/>";
break;
default:
break;
}
rv += click;
return rv;
}
public string MakeScriptButton(ButtonTypesEnum buttonType, string buttonText, string script = "", string buttonID = null, string url = null, string buttonClass = null, string style=null)

View File

@ -610,6 +610,7 @@ layui.use(['table'], function(){{
if(res.Code != undefined && res.Code != 200){{ layui.layer.alert(res.Msg,{{title:'{THProgram._localizer["Sys.Error"]}'}});}}
var tab = $('#{Id} + .layui-table-view');tab.find('table').css('border-collapse','separate');
{(Height == null ? $"tab.css('overflow','hidden').addClass('donotuse_fill donotuse_pdiv');tab.children('.layui-table-box').addClass('donotuse_fill donotuse_pdiv').css('height','100px');tab.find('.layui-table-main').addClass('donotuse_fill');tab.find('.layui-table-header').css('min-height','{maxDepth*38}px');ff.triggerResize();" : string.Empty)}
{(LineHeight.HasValue == true ? $"tab.find('td .layui-table-cell').css('height','{LineHeight}px')" : string.Empty)}
{(MultiLine == true ? $"tab.find('.layui-table-cell').css('height','auto').css('white-space','normal');" : string.Empty)}
tab.find('div [lay-event=\'LAYTABLE_COLS\']').attr('title','{THProgram._localizer["Sys.ColumnFilter"]}');
tab.find('div [lay-event=\'LAYTABLE_PRINT\']').attr('title','{THProgram._localizer["Sys.Print"]}');

View File

@ -28,7 +28,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
/// 启用搜索
/// 注意:多选与搜索不能同时启用
/// </summary>
public bool EnableSearch { get; set; }
public bool? EnableSearch { get; set; }
public ModelExpression Items { get; set; }
@ -40,9 +40,9 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
/// <summary>
/// 是否多选
/// 默认根据Field 绑定的值类型进行判断。Array or List 即多选,否则单选
/// 注意:多选与搜索不能同时启用
/// </summary>
public bool? MultiSelect { get; set; }
public bool AutoRow { get; set; }
/// <summary>
/// 改变选择时触发的js函数func(data)格式;
@ -65,16 +65,19 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
EmptyText = THProgram._localizer["Sys.PleaseSelect"];
}
if (EnableSearch == null)
{
EnableSearch = configs.CurrentValue.UIOptions.ComboBox.DefaultEnableSearch;
}
_wtm = wtm;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "select";
output.TagName = "div";
output.Attributes.Add("id", Id);
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add("name", Field.Name);
output.Attributes.Add("lay-filter", $"_WTMMultiCombo_{Guid.NewGuid()}_" + Field.Name);
output.Attributes.Add("wtm-name", Field.Name);
output.Attributes.Add("wtm-ctype", "combo");
if (Disabled == true)
@ -92,14 +95,6 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
MultiSelect = true;
}
}
if (MultiSelect.Value)
{
output.Attributes.Add("wtm-combo", "MULTI_COMBO");
}
if (!MultiSelect.Value && EnableSearch)
{
output.Attributes.Add("lay-search", string.Empty);
}
if (string.IsNullOrEmpty(ChangeFunc) == false)
{
output.Attributes.Add("wtm-cf", FormatFuncName(ChangeFunc, false));
@ -122,12 +117,8 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
output.Attributes.Add("wtm-turl", TriggerUrl);
}
var contentBuilder = new StringBuilder();
if (string.IsNullOrEmpty(EmptyText) == false)
{
contentBuilder.Append($"<option value=''>{EmptyText}</option>");
}
output.PostElement.AppendHtml($@"<input type=""hidden"" name=""_DONOTUSE_{Field.Name}"" value=""1"" />");
output.PreElement.AppendHtml($@"<input type=""hidden"" name=""_DONOTUSE_{Field.Name}"" value=""1"" />");
#region
@ -239,44 +230,109 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
}
}
}
if (MultiSelect == true)
{
foreach (var item in listItems)
{
contentBuilder.Append($"<option value='{item.Value}'{(string.IsNullOrEmpty(item.Icon) ? string.Empty : $" icon='{item.Icon}'")}>{item.Text}</option>");
}
// 添加默认选中项
var selected = listItems.Where(x => x.Selected).ToList();
var mulvalues = selected.ToSepratedString(x => x.Value, seperator: "`");
var mulnamess = selected.ToSepratedString(x => x.Text, seperator: "`");
output.Attributes.Add("wtm-combovalue", $"{mulvalues}");
output.Attributes.Add("wtm-comboname", $"{mulnamess}");
}
else // 添加用户设置的设置源
{
foreach (var item in listItems)
{
if (item.Selected == true)
{
if (Disabled == true)
{
output.PostElement.AppendHtml($"<input name='{Field.Name}' value='{item.Value}' text='{item.Text}' type='hidden' />");
}
contentBuilder.Append($"<option value='{item.Value}'{(string.IsNullOrEmpty(item.Icon) ? string.Empty : $" icon='{item.Icon}'")} selected>{item.Text}</option>");
}
else
{
contentBuilder.Append($"<option value='{item.Value}'{(string.IsNullOrEmpty(item.Icon) ? string.Empty : $" icon='{item.Icon}'")} {(item.Disabled == true ? "disabled=\"\"" : string.Empty)}>{item.Text}</option>");
}
}
}
output.Content.SetHtmlContent(contentBuilder.ToString());
var script = $@"
<script>
var {Id} = xmSelect.render({{
el: '#{Id}',
name:'{Field.Name}',
tips:'{EmptyText}',
disabled: {Disabled.ToString().ToLower()},
{(THProgram._localizer["Sys.LayuiDateLan"] =="CN"? "language:'zn'," : "language:'en',")}
autoRow: {AutoRow.ToString().ToLower()},
filterable: {EnableSearch.ToString().ToLower()},
template({{ item, sels, name, value }}){{
if(item.icon !== undefined && item.icon != """"&& item.icon != null){{
return '<i class=""'+item.icon+'""></i>' + item.name;
}}
else{{
return item.name;
}}
}},
{(MultiSelect == false ? $@"
radio: true,
clickClose: true,
model: {{
label: {{
type: 'abc' ,
abc: {{
template: function(item, sels){{
if(sels[0].icon !== undefined && sels[0].icon != """" && sels[0].icon != null){{
return '<i class=""'+sels[0].icon+'""></i>' + sels[0].name;
}}
else{{
return sels[0].name;
}}
}}
}}
}}
}},
toolbar: {{
show: true,
list: ['CLEAR']}}," : $@"
toolbar: {{show: true,list: ['ALL', 'REVERSE', 'CLEAR']}},
model: {{
label: {{
block: {{
template: function(item, sels){{
if(item.icon !== undefined && item.icon != """"&& item.icon != null){{
return '<i class=""'+item.icon+'""></i>' + item.name;
}}
else{{
return item.name;
}}
}},
}},
}}
}},
")}
height: '400px',
on:function(data){{
debugger;
{((LinkField != null || string.IsNullOrEmpty(LinkId) == false)?@$"
if (eval(""{(string.IsNullOrEmpty(ChangeFunc)?"1==1":FormatFuncName(ChangeFunc))}"") != false) {{
var u = ""{(TriggerUrl??"")}"";
if (u.indexOf(""?"") == -1) {{
u += ""?t="" + new Date().getTime();
}}
for (var i = 0; i < data.arr.length; i++) {{
u += ""&id="" + data.arr[i].value;
}}
ff.ChainChange(u, $('#{Id}')[0])
}}" : FormatFuncName(ChangeFunc))}
}},
data: {JsonSerializer.Serialize(GetLayuiTree(listItems,selectVal))}
}})
</script>
";
output.PostElement.AppendHtml(script);
#endregion
base.Process(context, output);
}
private List<LayuiTreeItem> GetLayuiTree(IEnumerable<ComboSelectListItem> tree, List<string> values)
{
List<LayuiTreeItem> rv = new List<LayuiTreeItem>();
foreach (var s in tree)
{
var news = new LayuiTreeItem
{
Id = s.Value.ToString(),
Title = s.Text,
Disabled = s.Disabled,
Checked = s.Selected,
Icon = s.Icon
};
if (values.Contains(s.Value.ToString().ToLower()))
{
news.Checked = true;
}
rv.Add(news);
}
return rv;
}
}
}

View File

@ -1,3 +1,4 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using WalkingTec.Mvvm.Core;
@ -201,7 +202,7 @@ $('#{search.SearchBtnId}').on('click', function () {{
firstkey = key;
}
output.PostElement.AppendHtml($@"
$(""#{Id}"").find(""button[type=submit]:first"").parent().prepend(""<div class='layui-input-block' style='text-align:left'><label style='color:red'>{error.ErrorMessage}</label></div>"");
$(""#{Id}"").find(""button[type=submit]:first"").parent().prepend(""<div class='layui-input-block' style='text-align:left'><label style='color:red'>{Regex.Replace(error.ErrorMessage,"<script>","", RegexOptions.IgnoreCase)}</label></div>"");
");
}
if (haserror == true)

View File

@ -43,9 +43,16 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
var listItems = new List<ComboSelectListItem>();
List<string> values = new List<string>();
if (modeltype.IsBoolOrNullableBool())
{
if (Field.Model == null)
{
values.Add("False");
}
else
{
values.Add(Field.Model.ToString());
}
}
else
{
if (Field.Model != null)

View File

@ -147,9 +147,15 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
}
if(selectVal.Count > 0)
{
DefaultValue = null;
DefaultValue = $"[{string.Join(",", selectVal.Select(x => "'" + x + "'"))}]";
}
else
{
if(string.IsNullOrEmpty(DefaultValue) == false)
{
DefaultValue = $"[{string.Join(",", DefaultValue.Split(",").Select(x => "'" + x + "'"))}]";
}
}
var title = $"['{(string.IsNullOrEmpty(LeftTitle) ? THProgram._localizer["Sys.ForSelect"] : LeftTitle)}','{(string.IsNullOrEmpty(RightTitle) ? THProgram._localizer["Sys.Selected"] : RightTitle)}']";
var content = $@"
<script>

View File

@ -5,7 +5,7 @@ using WalkingTec.Mvvm.Core.Extensions;
namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
[HtmlTargetElement("wt:image", Attributes = REQUIRED_ATTR_NAME, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("wt:image", TagStructure = TagStructure.WithoutEndTag)]
public class ImageTagHelper : BaseFieldTag
{
@ -26,7 +26,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
//TODO 若 image 组件未在form中该如何解决 _DONOT_USE_CS 的问题?
}
if (string.IsNullOrEmpty(Url) && Field.Model != null)
if (string.IsNullOrEmpty(Url) && Field?.Model != null)
{
Url = $"/_Framework/GetFile/{Field.Model}";
if (vm != null)
@ -36,7 +36,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
}
output.TagName = "img";
output.TagMode = TagMode.SelfClosing;
output.Attributes.Add("name", Field.Name + "img");
output.Attributes.Add("name", Field?.Name + "img");
output.Attributes.Add("id", Id + "img");
if (!string.IsNullOrEmpty(Url))
output.Attributes.Add("src", Url);

View File

@ -7,17 +7,44 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
public class LayuiTreeItem
{
[JsonPropertyName("title")]
[JsonPropertyName("name")]
public string Title { get; set; }
[JsonPropertyName("value")]
public string Id { get; set; }
[JsonPropertyName("children")]
public List<LayuiTreeItem> Children { get; set; }
[JsonPropertyName("href")]
public string Url { get; set; }
[JsonPropertyName("spread")]
public bool Expand { get; set; }
[JsonPropertyName("selected")]
public bool Checked { get; set; }
[JsonPropertyName("disabled")]
public bool Disabled { get; set; }
[JsonPropertyName("level")]
public int Level { get; set; }
[JsonPropertyName("icon")]
public string Icon { get; set; }
}
public class LayuiTreeItem2
{
[JsonPropertyName("title")]
public string Title { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("children")]
public List<LayuiTreeItem> Children { get; set; }
public List<LayuiTreeItem2> Children { get; set; }
[JsonPropertyName("href")]
public string Url { get; set; }
@ -31,4 +58,5 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
[JsonPropertyName("level")]
public int Level { get; set; }
}
}

View File

@ -96,7 +96,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
levelfieldname = Regex.Replace(levelfieldname, ".*?Searcher\\.", "");
cusmtomclick = $@"
$.extend({gridid}defaultfilter.where,{{'{idfieldname}':data.data.id, '{levelfieldname}':data.data.level }});
layui.table.reload('{gridid}',{{where: {gridid}defaultfilter.where}});
layui.table.reload('{gridid}',{{url:{gridid}url, where: {gridid}defaultfilter.where}});
";
}
}
@ -109,7 +109,7 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
cusmtomclick = $"{FormatFuncName(ClickFunc)};";
}
List<LayuiTreeItem> treeitems = GetLayuiTree(mm);
List<LayuiTreeItem2> treeitems = GetLayuiTree(mm);
var onclick = $@"
,click: function(data){{
var ele = null;
@ -167,16 +167,15 @@ layui.use(['tree'],function(){{
base.Process(context, output);
}
private List<LayuiTreeItem> GetLayuiTree(IEnumerable<TreeSelectListItem> tree, int level = 0)
private List<LayuiTreeItem2> GetLayuiTree(IEnumerable<TreeSelectListItem> tree, int level = 0)
{
List<LayuiTreeItem> rv = new List<LayuiTreeItem>();
List<LayuiTreeItem2> rv = new List<LayuiTreeItem2>();
foreach (var s in tree)
{
var news = new LayuiTreeItem
var news = new LayuiTreeItem2
{
Id = s.Value.ToString(),
Title = s.Text,
Icon = s.Icon,
Url = s.Url,
Expand = s.Expended,
Level = level,
@ -196,7 +195,7 @@ layui.use(['tree'],function(){{
return rv;
}
private LayuiTreeItem GetSelectedItem(List<LayuiTreeItem> tree)
private LayuiTreeItem2 GetSelectedItem(List<LayuiTreeItem2> tree)
{
foreach (var item in tree)
{

View File

@ -6,12 +6,14 @@ using WalkingTec.Mvvm.Core;
using WalkingTec.Mvvm.Core.Extensions;
using System.Linq;
using System.Text.Json;
using Microsoft.Extensions.Options;
namespace WalkingTec.Mvvm.TagHelpers.LayUI
{
[HtmlTargetElement("wt:tree", TagStructure = TagStructure.WithoutEndTag)]
public class TreeTagHelper : BaseFieldTag
{
public string EmptyText { get; set; }
public ModelExpression Items { get; set; }
public bool ShowLine { get; set; } = true;
/// <summary>
@ -46,66 +48,32 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
/// </para>
/// </summary>
public string CheckFunc { get; set; }
public bool AutoRow { get; set; }
public bool? EnableSearch { get; set; }
public TreeTagHelper(IOptionsMonitor<Configs> configs)
{
if (EmptyText == null)
{
EmptyText = THProgram._localizer["Sys.PleaseSelect"];
}
if (EnableSearch == null)
{
EnableSearch = configs.CurrentValue.UIOptions.ComboBox.DefaultEnableSearch;
}
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
bool MultiSelect = false;
var type = Field.Metadata.ModelType;
if (type.IsArray || (type.IsGenericType && typeof(List<>).IsAssignableFrom(type.GetGenericTypeDefinition())))// Array or List
if (Field.Name.Contains("[") || type.IsArray || type.IsList())// Array or List
{
MultiSelect = true;
}
string oncheck = "";
string onclick = "";
if (MultiSelect != true)
{
var formid = "";
if (context.Items.ContainsKey("formid"))
{
formid = $",'{context.Items["formid"]}form'";
}
onclick = $@"
,click: function(data){{
var ele = data.elem.find('.layui-tree-main:first');
if(last{Id} != null){{
last{Id}.css('background-color','');
last{Id}.find('.layui-tree-txt').css('color','');
}}
$('#tree{Id}hidden').html('');
if(last{Id} != null && last{Id}.is(ele)){{
last{Id} = null;
}}
else{{
ele.css('background-color','#5fb878');
ele.find('.layui-tree-txt').css('color','#fff');
$('#tree{Id}hidden').append(""<input type='hidden' name='{Field?.Name}' value='""+data.data.id+""'/>"");
last{Id} = ele;
}}
{FormatFuncName(CheckFunc)};
}}";
}
else
{
onclick = $@"
,click: function(data){{
{FormatFuncName(ClickFunc)};
}}";
}
oncheck = $@"
,oncheck: function(data){{
if(loaded{Id} == false) return;
var checkData = layui.tree.getChecked('{Id}');
var ids = ff.getTreeChecked(checkData);
$('#tree{Id}hidden').html('');
for(var i=0;i<ids.length;i++){{
$('#tree{Id}hidden').append(""<input type='hidden' name='{Field?.Name}' value='""+ids[i]+""'/>"");
}}
{FormatFuncName(CheckFunc)};
}}";
output.TagName = "div";
output.Attributes.Add("id", "div" + Id);
output.Attributes.Add("id", Id);
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add("wtm-ctype", "tree");
output.Attributes.Add("wtm-name", Field.Name);
@ -134,31 +102,66 @@ namespace WalkingTec.Mvvm.TagHelpers.LayUI
}
var script = $@"
<script>
layui.use(['tree'],function(){{
var last{Id} = null;
var loaded{Id} = false;
layui.tree.render({{
id:'{Id}',elem: '#div{Id}',onlyIconControl:{(!MultiSelect).ToString().ToLower()}, showCheckbox:{MultiSelect.ToString().ToLower()},showLine:{ShowLine.ToString().ToLower()}
,data: {JsonSerializer.Serialize(treeitems)} {oncheck} {onclick}
}});
loaded{Id} = true;";
var defaultselect = "";
if (Field.Model != null) {
defaultselect = $@"
var selected = $(""div[data-id='{Field.Model.ToString()}']"");
var selected2 = selected.find('.layui-tree-main:first');
selected2.css('background-color','#5fb878');
selected2.find('.layui-tree-txt').css('color','#fff');
last{Id} = selected2;
";
}
if (MultiSelect == false && Field.Model != null)
{
script += defaultselect;
}
script += $@"
var {Id} = xmSelect.render({{
el: '#{Id}',
name:'{Field.Name}',
tips:'{EmptyText}',
{(THProgram._localizer["Sys.LayuiDateLan"] == "CN" ? "language:'zn'," : "language:'en',")}
autoRow: {AutoRow.ToString().ToLower()},
filterable: {EnableSearch.ToString().ToLower()},
template({{ item, sels, name, value }}){{
if(item.icon !== undefined && item.icon != """"&& item.icon != null){{
return '<i class=""'+item.icon+'""></i>' + item.name;
}}
else{{
return item.name;
}}
}},
{(MultiSelect == false ? $@"
radio: true,
clickClose: true,
model: {{
label: {{
type: 'abc' ,
abc: {{
template: function(item, sels){{
if(sels[0].icon !== undefined && sels[0].icon != """" && sels[0].icon != null){{
return '<i class=""'+sels[0].icon+'""></i>' + sels[0].name;
}}
else{{
return sels[0].name;
}}
}}
}}
}}
}},
toolbar: {{
show: true,
list: ['CLEAR']}}," : $@"
toolbar: {{show: true,list: ['ALL', 'REVERSE', 'CLEAR']}},
model: {{
label: {{
block: {{
template: function(item, sels){{
if(item.icon !== undefined && item.icon != """"&& item.icon != null){{
return '<i class=""'+item.icon+'""></i>' + item.name;
}}
else{{
return item.name;
}}
}},
}},
}}
}},
")} tree: {{
strict: false,
show: true,
showFolderIcon: true,
showLine: true,
indent: 20
}},
height: '400px',
data: {JsonSerializer.Serialize(treeitems)}
}})
</script>
";
@ -167,7 +170,6 @@ layui.use(['tree'],function(){{
{
output.PostElement.AppendHtml($@"<script>
ff.LoadComboItems('tree','{ItemUrl}','{Id}','{Field.Name}',{JsonSerializer.Serialize(vals)},function(){{
{defaultselect}
}})
</script>");
@ -206,6 +208,8 @@ ff.LoadComboItems('tree','{ItemUrl}','{Id}','{Field.Name}',{JsonSerializer.Seria
Title = s.Text,
Url = s.Url,
Expand = s.Expended,
Disabled = s.Disabled,
Icon = s.Icon
//Children = new List<LayuiTreeItem>()
};
if (values.Contains(s.Value.ToString()))

View File

@ -7,7 +7,6 @@
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Runtime" Version="2.2.0" />