diff --git a/Tnb.Server.sln b/Tnb.Server.sln
index 8b41da8c..f9236fb7 100644
--- a/Tnb.Server.sln
+++ b/Tnb.Server.sln
@@ -140,7 +140,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tnb.PerMgr.Entities", "PerM
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tnb.PerMgr.Interfaces", "PerMgr\Tnb.PerMgr.Interfaces\Tnb.PerMgr.Interfaces.csproj", "{F3656494-27D3-4BD7-B831-8D909DFBD7B9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tnb.VmodelEngine", "visualdev\Tnb.Vmodel\Tnb.VmodelEngine.csproj", "{437AE0E4-66AE-4627-9ACD-29F5BB9E6642}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tnb.Vengine", "visualdev\Tnb.Vengine\Tnb.Vengine.csproj", "{644FEE7B-A58A-40F3-A011-3B73235B81D6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -340,10 +340,10 @@ Global
{F3656494-27D3-4BD7-B831-8D909DFBD7B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3656494-27D3-4BD7-B831-8D909DFBD7B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3656494-27D3-4BD7-B831-8D909DFBD7B9}.Release|Any CPU.Build.0 = Release|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Release|Any CPU.Build.0 = Release|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -396,7 +396,7 @@ Global
{D41946CF-09C6-4CA4-A1F4-42E7E1538BF7} = {74AB6486-1090-4CC9-9D1A-F1245E3ECFC3}
{42AD083D-D199-4B09-ADD8-89251011C959} = {74AB6486-1090-4CC9-9D1A-F1245E3ECFC3}
{F3656494-27D3-4BD7-B831-8D909DFBD7B9} = {74AB6486-1090-4CC9-9D1A-F1245E3ECFC3}
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642} = {161853F8-ADB9-4281-B706-E2E23D40D0F1}
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6} = {161853F8-ADB9-4281-B706-E2E23D40D0F1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {646DDD1C-F143-42C2-894F-F5C7B3A0CE74}
diff --git a/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj b/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
index 7e3429bd..e3a54fc6 100644
--- a/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
+++ b/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
@@ -44,7 +44,6 @@
-
diff --git a/visualdev/Tnb.Vmodel/BaseAppService.cs b/visualdev/Tnb.Vengine/AppService/BaseAppService.cs
similarity index 93%
rename from visualdev/Tnb.Vmodel/BaseAppService.cs
rename to visualdev/Tnb.Vengine/AppService/BaseAppService.cs
index e6fa3d23..f657fff2 100644
--- a/visualdev/Tnb.Vmodel/BaseAppService.cs
+++ b/visualdev/Tnb.Vengine/AppService/BaseAppService.cs
@@ -6,7 +6,7 @@
using JNPF.DependencyInjection;
using JNPF.DynamicApiController;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine.AppService;
///
/// 增删改查基类
diff --git a/visualdev/Tnb.Vmodel/IVmodelAppService.cs b/visualdev/Tnb.Vengine/AppService/IVmodelAppService.cs
similarity index 93%
rename from visualdev/Tnb.Vmodel/IVmodelAppService.cs
rename to visualdev/Tnb.Vengine/AppService/IVmodelAppService.cs
index 746bd7f2..f00093b5 100644
--- a/visualdev/Tnb.Vmodel/IVmodelAppService.cs
+++ b/visualdev/Tnb.Vengine/AppService/IVmodelAppService.cs
@@ -5,7 +5,7 @@
using JNPF.DependencyInjection;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine.AppService;
///
/// 视图模型服务接口
diff --git a/visualdev/Tnb.Vmodel/IVmodelPageAppService.cs b/visualdev/Tnb.Vengine/AppService/IVmodelPageAppService.cs
similarity index 93%
rename from visualdev/Tnb.Vmodel/IVmodelPageAppService.cs
rename to visualdev/Tnb.Vengine/AppService/IVmodelPageAppService.cs
index 4b19ef28..33a0d1ce 100644
--- a/visualdev/Tnb.Vmodel/IVmodelPageAppService.cs
+++ b/visualdev/Tnb.Vengine/AppService/IVmodelPageAppService.cs
@@ -5,7 +5,7 @@
using JNPF.DependencyInjection;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine.AppService;
///
/// 视图模型服务接口
diff --git a/visualdev/Tnb.Vengine/AppService/VmAppService.cs b/visualdev/Tnb.Vengine/AppService/VmAppService.cs
new file mode 100644
index 00000000..1ef08ede
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmAppService.cs
@@ -0,0 +1,198 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using JNPF.Common.Security;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 增删改查基类
+///
+[ApiDescriptionSettings(Tag = ModuleConst.Tag, Area = ModuleConst.Area, Order = 10, KeepVerb = true)]
+[Route("api")]
+public class VmAppService : BaseAppService
+{
+ private readonly IDataAccess _dataAccess;
+ private readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public VmAppService(IDataAccess da)
+ {
+ _dataAccess = da;
+ _db = _dataAccess.GetSqlSugar();
+ }
+
+ #region 根据vmodel id进行增删改查接口
+ ///
+ /// 获取一条 数据信息
+ ///
+ [HttpGet("[area]/[controller]/{vmid}/[action]")]
+ public async Task GetAsync(string vmid, [FromQuery] VmGetInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid, true);
+ VmQueryInput arg = input.Adapt();
+ if (input.id != null)
+ {
+ if (arg.q == null) arg.q = new DObject();
+ arg.q.Add(vm.GetPrimary().code, input.id);
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls.items.FirstOrDefault();
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpGet("[area]/[controller]/{vmid}/[action]")]
+ public async Task GetListAsync(string vmid, [FromQuery] VmGetListInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid, true);
+ VmQueryInput arg = input.Adapt();
+ if (!string.IsNullOrEmpty(input.q))
+ {
+ arg.q = input.q.ToObject();
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpPost("[area]/[controller]/{vmid}/[action]")]
+ public async Task QueryAsync(string vmid, [FromBody] VmQueryInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid, true);
+ var ls = await _dataAccess.QueryDataAsync(vm, input);
+ return ls;
+ }
+
+ ///
+ /// 新增 数据
+ ///
+ [HttpPost("[area]/[controller]/{vmid}/[action]")]
+ public async Task CreateAsync(string vmid, VmCreateInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid);
+ var ret = await _dataAccess.CreateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ [HttpPut("[area]/[controller]/{vmid}/[action]")]
+ public async Task UpdateAsync(string vmid, VmUpdateInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid);
+ var ret = await _dataAccess.UpdateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ [HttpDelete("[area]/[controller]/{vmid}/[action]")]
+ public async Task DeleteAsync(string vmid, [FromQuery] VmDeleteInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid);
+ var ret = await _dataAccess.DeleteDataAsync(vm, input);
+ return ret;
+ }
+
+ private async Task GetVmodelAsync(string area, string vmCode)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(area.SnakeToPascalCase(), vmCode.SnakeToPascalCase(), true);
+ return vm;
+ }
+ #endregion
+
+ #region 根据vmode的area和code进行增删改查接口
+ ///
+ /// 获取一条 数据信息
+ ///
+ [HttpGet("{areaCode}/{vmCode}/[action]")]
+ public async Task GetAsync(string areaCode, string vmCode, [FromQuery] VmGetInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ VmQueryInput arg = input.Adapt();
+ if (input.id != null)
+ {
+ if (arg.q == null) arg.q = new DObject();
+ arg.q.Add(vm.GetPrimary().code, input.id);
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls.items.FirstOrDefault();
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpGet("{areaCode}/{vmCode}/[action]")]
+ public async Task GetListAsync(string areaCode, string vmCode, [FromQuery] VmGetListInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ VmQueryInput arg = input.Adapt();
+ if (!string.IsNullOrEmpty(input.q))
+ {
+ arg.q = input.q.ToObject();
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpPost("{areaCode}/{vmCode}/[action]")]
+ public async Task QueryAsync(string areaCode, string vmCode, [FromBody] VmQueryInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ls = await _dataAccess.QueryDataAsync(vm, input);
+ return ls;
+ }
+
+ ///
+ /// 新增 数据
+ ///
+ [HttpPost("{areaCode}/{vmCode}/[action]")]
+ public async Task CreateAsync(string areaCode, string vmCode, VmCreateInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ret = await _dataAccess.CreateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ [HttpPut("{areaCode}/{vmCode}/[action]")]
+ public async Task UpdateAsync(string areaCode, string vmCode, VmUpdateInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ret = await _dataAccess.UpdateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ [HttpDelete("{areaCode}/{vmCode}/[action]")]
+ public async Task DeleteAsync(string areaCode, string vmCode, [FromQuery] VmDeleteInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ret = await _dataAccess.DeleteDataAsync(vm, input);
+ return ret;
+ }
+ #endregion
+
+}
diff --git a/visualdev/Tnb.Vengine/AppService/VmAppServiceT.cs b/visualdev/Tnb.Vengine/AppService/VmAppServiceT.cs
new file mode 100644
index 00000000..4919858d
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmAppServiceT.cs
@@ -0,0 +1,120 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using JNPF.Common.Contracts;
+using JNPF.Common.Security;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 增删改查基类
+///
+[ApiDescriptionSettings(Area = ModuleConst.Area, Order = 10, KeepVerb = true)]
+[Route("api/[area]/[controller]/[action]")]
+public class VmAppService : BaseAppService where TEntity : IEntity
+{
+ protected readonly IDataAccess _dataAccess;
+ protected readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public VmAppService(IDataAccess dataAccess)
+ {
+ _dataAccess = dataAccess;
+ _db = _dataAccess.GetSqlSugar();
+ }
+
+ protected async Task GetVmodelAsync()
+ {
+ var tp = typeof(TEntity);
+ if (string.IsNullOrEmpty(tp?.Namespace))
+ {
+ throw new ArgumentNullException($"类型 {nameof(tp)} 的命名空间不可为空");
+ }
+ var area = tp.Namespace.Split('.').Last().ToKebabCase();
+ var vm = await _dataAccess.GetVmodelAsync(area, tp.Name, true);
+
+ return vm;
+ }
+
+ ///
+ /// 获取一条 数据信息
+ ///
+ public virtual async Task GetAsync([FromQuery] VmGetInput input)
+ {
+ var vm = await GetVmodelAsync();
+ VmQueryInput arg = input.Adapt();
+ if (input.id != null)
+ {
+ if (arg.q == null) arg.q = new DObject();
+ arg.q.Add(vm.GetPrimary().code, input.id);
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls.items.FirstOrDefault()!;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ public virtual async Task GetListAsync([FromQuery] VmGetListInput input)
+ {
+ var vm = await GetVmodelAsync();
+ VmQueryInput arg = input.Adapt();
+ if (!string.IsNullOrEmpty(input.q))
+ {
+ arg.q = input.q.ToObject();
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpPost]
+ public virtual async Task QueryAsync([FromBody] VmQueryInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ls = await _dataAccess.QueryDataAsync(vm, input);
+ return ls;
+ }
+
+ ///
+ /// 新增 数据
+ ///
+ public virtual async Task CreateAsync(VmCreateInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ret = await _dataAccess.CreateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ public virtual async Task UpdateAsync(VmUpdateInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ret = await _dataAccess.UpdateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ public virtual async Task DeleteAsync([FromQuery] VmDeleteInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ret = await _dataAccess.DeleteDataAsync(vm, input);
+ return ret;
+ }
+
+}
diff --git a/visualdev/Tnb.Vengine/AppService/VmodelAppService.cs b/visualdev/Tnb.Vengine/AppService/VmodelAppService.cs
new file mode 100644
index 00000000..d77245c3
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmodelAppService.cs
@@ -0,0 +1,151 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 视图模型服务类
+///
+public class VmodelAppService : VmAppService, IVmodelAppService
+{
+
+ ///
+ /// 构造函数
+ ///
+ public VmodelAppService(IDataAccess da) : base(da)
+ {
+ }
+
+ ///
+ /// 获取一条 数据信息
+ ///
+ public override async Task GetAsync(VmGetInput input)
+ {
+ //return await _dataAccess.GetVmodelAsync(input.id);
+ var query = _db.Queryable().Where(a => a.deleted == 0);
+ Vmodel vm;
+ if (long.TryParse(input.id, out long id))
+ {
+ vm = await query.FirstAsync(a => a.id == input.id);
+ }
+ else
+ {
+ vm = await query.FirstAsync(a => a.vmCode == input.id);
+ }
+ return vm;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ public override async Task GetListAsync(VmGetListInput input)
+ {
+ VmPagedOutput ret = new();
+ var q = _db.Queryable().WhereIF(!string.IsNullOrEmpty(input.k), a => a.vmCode.Contains(input.k!) || a.vmName.Contains(input.k!));
+ RefAsync total = 0;
+ var data = await q.OrderBy(input.sort).ToPageListAsync((input.pnum - 1) * input.psize, input.psize, total);
+ ret.total = total;
+ ret.items = data.ConvertAll(a => a);
+ return ret;
+ }
+
+ [NonAction]
+ public override Task QueryAsync(VmQueryInput input)
+ {
+ return base.QueryAsync(input);
+ }
+
+ ///
+ /// 新增 模型
+ ///
+ public override async Task CreateAsync(VmCreateInput input)
+ {
+ //ThrowIf.IsNull(input.data, nameof(input));
+ ArgumentNullException.ThrowIfNull(input.data);
+ Vmodel vm = input.data.Adapt();
+ await _db.Insertable(vm).ExecuteCommandAsync();
+ return input;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ public override async Task UpdateAsync(VmUpdateInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.data);
+ Vmodel vm = input.data.Adapt();
+ await _db.Updateable(vm).WhereColumns(a => a.id).ExecuteCommandAsync();
+ return input;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ public override async Task DeleteAsync(VmDeleteInput input)
+ {
+ var ret = await _db.Deleteable(input.id).ExecuteCommandAsync();
+ return ret;
+ }
+
+ ///
+ /// 从数据表创建模型
+ ///
+ public async Task> CreateFromTable(VmodelCreateFromTableInput input)
+ {
+ ThrowIf.IsNull(input.tableName, nameof(input.tableName));
+ var sugar = _dataAccess.GetSqlSugar(input.dbCode);
+ var lsTable = sugar.DbMaintenance.GetTableInfoList().WhereIF(input.tableName != "ALL", a => a.Name == input.tableName);
+
+ List lsToAdd = new List();
+ List lsToUpdate = new List();
+ foreach (var tb in lsTable)
+ {
+ if (!string.IsNullOrEmpty(input.removePrefix) && !tb.Name.StartsWith(input.removePrefix)) continue;
+ var colInfo = sugar.DbMaintenance.GetColumnInfosByTableName(tb.Name);
+ Vmodel model = new() { dbCode = input.dbCode, vmName = tb.Description, tableName = tb.Name };
+ model.area = input.area;
+ model.vmCode = (string.IsNullOrEmpty(input.removePrefix) ? tb.Name : tb.Name.RemovePreFix(input.removePrefix)).SnakeToPascalCase();
+ //model.createId = CurrentUser.Id;
+ int n = 1;
+ foreach (var p in colInfo)
+ {
+ var prop = p.Adapt();
+ prop.ordinal = n++;
+ prop.csType = sugar.Ado.DbBind.GetPropertyTypeName(p.DataType);
+ model.dbProps.Add(prop);
+ }
+ var exist = await _db.Queryable().FirstAsync(a => a.dbCode == input.dbCode && a.tableName == tb.Name);
+ if (exist == null)
+ {
+ lsToAdd.Add(model);
+ }
+ else
+ {
+ exist.area = model.area;
+ model.dbProps.Adapt(exist.dbProps);
+ //exist.dbProps.Clear();
+ //exist.dbProps.AddRange(model.dbProps.OrderBy(a => a.ordinal));
+ lsToUpdate.Add(exist);
+ }
+ }
+ if (lsToAdd.Count > 0)
+ {
+ await _db.Insertable(lsToAdd).ExecuteCommandAsync();
+ }
+ if (lsToUpdate.Count > 0)
+ {
+ await _db.Updateable(lsToUpdate).ExecuteCommandAsync();
+ }
+ return lsToAdd.Union(lsToUpdate).ToList();
+ }
+
+
+}
diff --git a/visualdev/Tnb.Vengine/AppService/VmodelPageAppService.cs b/visualdev/Tnb.Vengine/AppService/VmodelPageAppService.cs
new file mode 100644
index 00000000..553960f8
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmodelPageAppService.cs
@@ -0,0 +1,165 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using System.Text;
+using JNPF.Common.Security;
+using Mapster;
+using Newtonsoft.Json.Linq;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 视图模型服务类
+///
+public class VmodelPageAppService : VmAppService, IVmodelPageAppService
+{
+ ///
+ /// 构造函数
+ ///
+ public VmodelPageAppService(IDataAccess da) : base(da)
+ {
+ }
+
+ ///
+ /// 获取一条 数据信息
+ ///
+ public override async Task GetAsync(VmGetInput input)
+ {
+ var query = _db.Queryable().Where(a => a.deleted == 0);
+ VmodelPage vm = await query.FirstAsync(a => a.id == input.id);
+ return vm;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ public override async Task GetListAsync(VmGetListInput input)
+ {
+ VmPagedOutput ret = new();
+ var q = _db.Queryable().WhereIF(!string.IsNullOrEmpty(input.k), a => a.code.Contains(input.k!) || a.name.Contains(input.k!));
+ RefAsync total = 0;
+ var data = await q.OrderBy(input.sort).ToPageListAsync((input.pnum - 1) * input.psize, input.psize, total);
+ ret.total = total;
+ ret.items = data.ConvertAll(a => a);
+ return ret;
+ }
+
+ ///
+ /// 新增 模型
+ ///
+ public override async Task CreateAsync(VmCreateInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.data);
+ VmodelPage vpage = input.data.Adapt();
+ await _db.Insertable(vpage).ExecuteCommandAsync();
+ return vpage;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ public override async Task UpdateAsync(VmUpdateInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.data);
+ if (!input.data.ContainsKey(nameof(VmodelPage.id)))
+ {
+ throw new Exception($"更新数据时主键({nameof(VmodelPage.id)})不可为空");
+ }
+ var id = input.data[nameof(VmodelPage.id)].ToString();
+ var model = await _db.Queryable().FirstAsync(a => a.id == id);
+ ArgumentNullException.ThrowIfNull(model);
+ input.data.Adapt(model, TypeAdapter.IgnoreNull);
+ await _db.Updateable(model).WhereColumns(a => a.id).ExecuteCommandAsync();
+ return model;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ public override async Task DeleteAsync(VmDeleteInput input)
+ {
+ var ret = await _db.Deleteable(input.id).ExecuteCommandAsync();
+ return ret;
+ }
+
+ ///
+ /// 从数据表创建模型
+ ///
+ public async Task CreateByVmodel(CreatePageFromVmodelInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.vmid);
+ var vm = await _dataAccess.GetVmodelAsync(input.vmid);
+ ArgumentNullException.ThrowIfNull(vm);
+
+ var page = await _db.Queryable().FirstAsync(a => a.vmid == vm.id);
+ if (page == null)
+ {
+ page = new VmodelPage { vmid = vm.id, code = vm.vmCode, name = vm.vmName };
+ page.pageSchema = CreatePageSchema(vm, page.id);
+ await _db.Insertable(page).ExecuteCommandAsync();
+ }
+ else
+ {
+ page.pageSchema = CreatePageSchema(vm, page.id);
+ await _db.Updateable(page).ExecuteCommandAsync();
+ }
+ return page;
+ }
+
+ private JObject CreatePageSchema(Vmodel vm, string pageid)
+ {
+ StringBuilder str = new StringBuilder();
+ str.AppendLine("{");
+ str.AppendLine($"\"page\": {{ \"loadList\": true, \"watchClient\": false }},");
+ str.AppendLine($"\"queryData\": {{ }},");
+ str.AppendLine($"\"queryForm\": {{");
+ str.AppendLine($"\"show\": false,");
+ str.AppendLine($"\"attr\": {{ \"labelWidth\": \"106px\", \"hasKeyword\":false }},");
+ str.AppendLine($"\"cols\": {{");
+ var pQuery = vm.dbProps.Skip(1).Take(1).FirstOrDefault();
+ if (pQuery != null)
+ {
+ str.AppendLine($"\"{pQuery.code}\": {{ \"label\": \"{pQuery.name}\", \"span\": 8, \"qtype\": 2, \"isQuick\": true, \"comp\": {{ \"type\": \"el-input\", \"attr\": {{ \"placeholder\": \"{pQuery.name}\", \"clearable\": true, \"maxlength\": 20 }} }} }}");
+ }
+ str.AppendLine($"}}");
+ str.AppendLine($"}},");
+ str.AppendLine($"\"list\": {{");
+ str.AppendLine($"\"opt\": {{ \"isPage\": true, \"isCheck\": true, \"sortBy\": \"\", \"pkey\": \"{vm.GetPrimary().code}\" }},");
+ str.AppendLine($"\"attr\": {{ \"border\": false }},");
+ str.AppendLine($"\"cols\": {{");
+ foreach (var p in vm.dbProps)
+ {
+ str.AppendLine($"\"{p.code}\":{{ \"label\": \"{p.name}\", \"show\": true, \"attr\": {{ {p.GetDefaultWidth()} }}, \"comp\": {{}} }},");
+ }
+ str.AppendLine($"}}");
+ str.AppendLine($"}},");
+ str.AppendLine($"\"editData\": {vm.GetDefaultDObject().ToJsonString()},");
+ str.AppendLine($"\"editDlg\": {{ \"isAdd\": true, \"tabHeight\": 300, \"name\": \"{vm.vmName}\" }},");
+ str.AppendLine($"\"editForm\": {{");
+ str.AppendLine($"\"attr\": {{ \"labelWidth\": \"106px\" }},");
+ str.AppendLine($"\"rules\": {{");
+ foreach (var p in vm.dbProps.Where(a => a.required && !a.pkey))
+ {
+ str.AppendLine($"\"{p.code}\": [{{ \"required\": true, \"message\": \"必填项不能为空\", \"trigger\": \"blur\" }}],");
+ }
+ str.AppendLine($"}},");
+ str.AppendLine($"\"cols\": {{");
+ foreach (var p in vm.dbProps)
+ {
+ str.AppendLine($"\"{p.code}\": {{ \"label\": \"{p.name}\", \"show\": true, \"comp\": {p.GetDefaultComp().ToJsonString()} }},");
+ }
+ str.AppendLine($"}}");
+ str.AppendLine($"}},");
+ str.AppendLine($"\"tree\": {{ \"key\": \"id\", \"height\": 300, \"props\": {{ \"label\": \"enumName\" }}, \"data\": [] }}");
+ str.AppendLine($"}}");
+ var s = str.ToString();
+ Console.WriteLine(s);
+ return JObject.Parse(s);
+ }
+
+}
diff --git a/visualdev/Tnb.Vmodel/Constants/DbConsts.cs b/visualdev/Tnb.Vengine/Constants/DbConsts.cs
similarity index 99%
rename from visualdev/Tnb.Vmodel/Constants/DbConsts.cs
rename to visualdev/Tnb.Vengine/Constants/DbConsts.cs
index 162f3f70..bed8b683 100644
--- a/visualdev/Tnb.Vmodel/Constants/DbConsts.cs
+++ b/visualdev/Tnb.Vengine/Constants/DbConsts.cs
@@ -3,7 +3,7 @@
// https://git.tuotong-tech.com/tnb/tnb.server //
/////////////////////////////////////////////////////////////////////////////////
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine;
public static class DbConsts
{
diff --git a/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs b/visualdev/Tnb.Vengine/Constants/ModuleConst.cs
similarity index 77%
rename from visualdev/Tnb.Vmodel/Constants/ModuleConst.cs
rename to visualdev/Tnb.Vengine/Constants/ModuleConst.cs
index 1e99e591..52c2abbe 100644
--- a/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs
+++ b/visualdev/Tnb.Vengine/Constants/ModuleConst.cs
@@ -1,4 +1,4 @@
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine;
public class ModuleConst
{
diff --git a/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs b/visualdev/Tnb.Vengine/Constants/VmodelEnum.cs
similarity index 98%
rename from visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs
rename to visualdev/Tnb.Vengine/Constants/VmodelEnum.cs
index f4e4fe1b..c328ad33 100644
--- a/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs
+++ b/visualdev/Tnb.Vengine/Constants/VmodelEnum.cs
@@ -5,7 +5,7 @@
using System.ComponentModel;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine;
public enum eCsType
{
diff --git a/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs b/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs
new file mode 100644
index 00000000..6d287556
--- /dev/null
+++ b/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs
@@ -0,0 +1,422 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using System.Collections.Concurrent;
+using JNPF;
+using JNPF.DependencyInjection;
+using Mapster;
+using SqlSugar;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.DataAccess;
+
+///
+///
+///
+public class DataAccess : IDataAccess, ITransient, IDisposable
+{
+ const int MAX_PAGE_SIZE = 1000;
+ private ISqlSugarClient? sugar;
+ protected ISqlSugarClient Db
+ {
+ get
+ {
+ if (sugar == null)
+ {
+ ConnectionStringsOptions conn = App.GetConfig("ConnectionStrings", true);
+ //var DBType = (DbType)Enum.Parse(typeof(DbType), conn.DBType);
+ sugar = new SqlSugarScope(new ConnectionConfig
+ {
+ ConnectionString = conn.ConnectString,
+ DbType = conn.DBType.Adapt(),
+ IsAutoCloseConnection = true,
+ ConfigId = conn.ConfigId,
+ InitKeyType = InitKeyType.Attribute,
+ MoreSettings = new ConnMoreSettings()
+ {
+ IsAutoRemoveDataCache = true, // 自动清理缓存
+ IsAutoToUpper = false,
+ PgSqlIsAutoToLower = false,
+ DisableNvarchar = true
+ },
+ }, SugarHelper.ConfigSugar);
+ }
+ return sugar;
+ }
+ }
+
+ ///
+ /// 全局缓存
+ ///
+ static ConcurrentDictionary DbCache = new ConcurrentDictionary();
+
+ ///
+ /// 构造
+ ///
+ public DataAccess()
+ {
+ }
+
+ ///
+ /// 释放
+ ///
+ public void Dispose()
+ {
+ foreach (var item in DbCache)
+ {
+ item.Value.Dispose();
+ }
+ DbCache.Clear();
+ }
+
+ ///
+ /// 获取 ISqlSugarClient
+ ///
+ public ISqlSugarClient GetSqlSugar(string? dbCode = null)
+ {
+ if (string.IsNullOrEmpty(dbCode) || dbCode == DbConsts.DefaultDbCode)
+ {
+ return Db;
+ }
+ if (DbCache.ContainsKey(dbCode))
+ {
+ return DbCache[dbCode];
+ }
+
+ var dblink = GetVmLink(dbCode);
+ if (dblink == null)
+ {
+ throw new Exception($"没有此数据库{dbCode}连接信息");
+ }
+ var dbType = Enum.Parse(dblink.dbType.ToString(), true);
+ var sugar = new SqlSugarScope(new ConnectionConfig
+ {
+ ConnectionString = dblink.dbConnection,
+ DbType = dbType,
+ IsAutoCloseConnection = true,
+ ConfigId = dblink.dbCode,
+ InitKeyType = InitKeyType.Attribute,
+ MoreSettings = new ConnMoreSettings()
+ {
+ IsAutoRemoveDataCache = true, // 自动清理缓存
+ IsAutoToUpper = false,
+ PgSqlIsAutoToLower = false,
+ DisableNvarchar = true
+ },
+ }, SugarHelper.ConfigSugar);
+ if (sugar.Ado.IsValidConnection())
+ {
+ DbCache[dbCode] = sugar;
+ }
+ else
+ {
+ sugar.Dispose();
+ throw new Exception($"无法连接到数据库{dbCode}");
+ }
+ return DbCache[dbCode];
+ }
+
+ ///
+ /// 获取 DbLink
+ ///
+ public VmodelLink GetVmLink(string dbCode)
+ {
+ var model = Db.Queryable().First(a => a.dbCode == dbCode);
+ return model;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时不抛异常
+ ///
+ public async Task TryGetVmodelAsync(string id, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0);
+ if (vm != null && loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+ return vm;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时抛异常
+ ///
+ public async Task GetVmodelAsync(string id, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0);
+ ArgumentNullException.ThrowIfNull(vm, $"找不到vmid={id}的模型");
+ if (loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+ return vm;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时不抛异常
+ ///
+ public async Task TryGetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
+ if (vm != null && loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+
+ return vm;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时抛异常
+ ///
+ public async Task GetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
+ ArgumentNullException.ThrowIfNull(vm, $"找不到area={area}, vmCode={vmCode}的模型");
+ if (loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+
+ return vm;
+ }
+
+ /////
+ ///// 获取 Vmodel
+ /////
+ //public async Task GetVmodelAsync(string tableName, string? dbCode)
+ //{
+ // Vmodel vm = await _db.Queryable().FirstAsync(a => a.tableName == tableName && a.dbCode == dbCode && a.deleted == 0);
+ // return vm;
+ //}
+
+ ///
+ /// 加载模型的导航属性
+ ///
+ ///
+ ///
+ private async Task LoadVmodelNavigateAsync(Vmodel vm)
+ {
+ Dictionary dictVm = new();
+ foreach (var navProp in vm.navProps)
+ {
+ if (!dictVm.ContainsKey(navProp.vmid))
+ {
+ var navModel = await GetVmodelAsync(navProp.vmid);
+ dictVm.Add(navProp.vmid, navModel);
+ }
+ navProp.naviModel = dictVm[navProp.vmid];
+ }
+ }
+
+ ///
+ /// 查询数据 默认方法
+ ///
+ public async Task QueryDataAsync(Vmodel vm, VmQueryInput input)
+ {
+ ISqlSugarClient db = GetSqlSugar(vm.dbCode);
+ var query = db.Queryable