mirror of
https://gitee.com/dotnetchina/OpenAuth.Net.git
synced 2026-07-28 06:07:59 +08:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe152777d0 | ||
|
|
aeaaa29122 | ||
|
|
78b627ef18 | ||
|
|
16fd40174c | ||
|
|
935064f440 | ||
|
|
dde08ed0d7 | ||
|
|
61cf133db0 | ||
|
|
9a7d114658 | ||
|
|
41bbeb6b2f | ||
|
|
591ae5a09a | ||
|
|
cfe60345d2 | ||
|
|
0d098a9a29 | ||
|
|
c5df5a51fd | ||
|
|
a31d66d069 | ||
|
|
c2cdc65cbe | ||
|
|
7ab6c5393f | ||
|
|
08bf4ba5c4 | ||
|
|
eb77732a7d | ||
|
|
15c54f71a7 | ||
|
|
371efbbe87 | ||
|
|
5581f2bca9 | ||
|
|
22975a3f34 | ||
|
|
3946fc7e81 | ||
|
|
fd35eb0f63 | ||
|
|
eddb32c6b6 | ||
|
|
9b78b9e6c7 | ||
|
|
0cce0a8f43 | ||
|
|
a137aaca40 | ||
|
|
eb3d8bb4b3 | ||
|
|
9c0fa9d16a | ||
|
|
b23be0571f | ||
|
|
de12924fac | ||
|
|
c40f473d82 | ||
|
|
415da9f69c | ||
|
|
6ef7c91896 | ||
|
|
6ae0c4b9b2 |
17
.cursorignore
Normal file
17
.cursorignore
Normal file
@@ -0,0 +1,17 @@
|
||||
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
||||
/OpenAuth.App/bin
|
||||
/OpenAuth.App/obj
|
||||
/OpenAuth.Mvc/bin
|
||||
/OpenAuth.Mvc/obj
|
||||
/OpenAuth.WebApi/bin
|
||||
/OpenAuth.WebApi/obj
|
||||
/Infrastructure/bin
|
||||
/Infrastructure/obj
|
||||
/OpenAuth.Identity/bin
|
||||
/OpenAuth.Identity/obj
|
||||
/newdocs/node_modules
|
||||
/newdocs/docs/.vuepress/.cache
|
||||
/newdocs/docs/.vuepress/.temp
|
||||
/newdocs/docs/.vuepress/dist
|
||||
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -22,6 +22,12 @@ bld/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# docs
|
||||
node_modules/
|
||||
.cache/
|
||||
.temp/
|
||||
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace OpenAuth.App
|
||||
return result;
|
||||
}
|
||||
|
||||
public string Add(AddOrUpdateBuilderTableReq req)
|
||||
public void Check(AddOrUpdateBuilderTableReq req)
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.TableName))
|
||||
{
|
||||
@@ -112,11 +112,18 @@ namespace OpenAuth.App
|
||||
throw new Exception("模块名称不能为空");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(req.Namespace))
|
||||
if(!string.IsNullOrEmpty(req.ParentTableId))
|
||||
{
|
||||
throw new Exception("命名空间不能为空");
|
||||
if(string.IsNullOrEmpty(req.ForeignKey))
|
||||
{
|
||||
throw new Exception("当前表为子表时,外键不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Add(AddOrUpdateBuilderTableReq req)
|
||||
{
|
||||
Check(req);
|
||||
var obj = AddTableAndColumns(req.MapTo<BuilderTable>());
|
||||
|
||||
UnitWork.Save();
|
||||
@@ -176,6 +183,7 @@ namespace OpenAuth.App
|
||||
|
||||
public void Update(AddOrUpdateBuilderTableReq obj)
|
||||
{
|
||||
Check(obj);
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
UnitWork.Update<BuilderTable>(u => u.Id == obj.Id, u => new BuilderTable
|
||||
{
|
||||
@@ -272,32 +280,103 @@ namespace OpenAuth.App
|
||||
|
||||
CheckExistsModule(sysTableInfo.ModuleCode);
|
||||
|
||||
string domainContent = string.Empty;
|
||||
if (sysTableInfo.IsDynamicHeader) //使用动态头部的模版
|
||||
string updateColumns = string.Empty;
|
||||
string insertColumns = string.Empty;
|
||||
foreach (var column in sysColumns)
|
||||
{
|
||||
domainContent = FileHelper.ReadFile(@"Template\\SingleTable\\BuildAppWithDynamicHeader.html");
|
||||
if(column.ColumnName.ToLower() == "id")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(column.ColumnName.ToLower() == "createtime" || column.ColumnName.ToLower() == "updatetime")
|
||||
{
|
||||
updateColumns += $" {column.ColumnName} = DateTime.Now,";
|
||||
insertColumns += $" obj.{column.ColumnName} = DateTime.Now;";
|
||||
updateColumns += "\r\n";
|
||||
insertColumns += "\r\n";
|
||||
continue;
|
||||
}
|
||||
if(column.ColumnName.ToLower() == "createuserid" || column.ColumnName.ToLower() == "updateuserid")
|
||||
{
|
||||
updateColumns += $" {column.ColumnName} = user.Id,";
|
||||
insertColumns += $" obj.{column.ColumnName} = user.Id;";
|
||||
updateColumns += "\r\n";
|
||||
insertColumns += "\r\n";
|
||||
continue;
|
||||
}
|
||||
if(column.ColumnName.ToLower() == "createusername" || column.ColumnName.ToLower() == "updateusername")
|
||||
{
|
||||
updateColumns += $" {column.ColumnName} = user.Name,";
|
||||
insertColumns += $" obj.{column.ColumnName} = user.Name;";
|
||||
updateColumns += "\r\n";
|
||||
insertColumns += "\r\n";
|
||||
continue;
|
||||
}
|
||||
//insert有map函数,所以不需要处理,只处理update即可
|
||||
updateColumns += $" {column.ColumnName} = request.{column.ColumnName},";
|
||||
updateColumns += "\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
domainContent = FileHelper.ReadFile(@"Template\\SingleTable\\BuildApp.html");
|
||||
}
|
||||
domainContent = domainContent
|
||||
.Replace("{TableName}", sysTableInfo.TableName)
|
||||
.Replace("{ModuleCode}", sysTableInfo.ModuleCode)
|
||||
.Replace("{ModuleName}", sysTableInfo.ModuleName)
|
||||
.Replace("{ClassName}", sysTableInfo.ClassName)
|
||||
.Replace("{StartName}", StratName);
|
||||
|
||||
if (!string.IsNullOrEmpty(sysTableInfo.ForeignKey))
|
||||
{ //替换外键模版
|
||||
var foreignTemplate = $"objs = objs.Where(u => u.{sysTableInfo.ForeignKey} == request.{sysTableInfo.ForeignKey});";
|
||||
string domainContent = string.Empty;
|
||||
//查找是否存在子表的情况
|
||||
var subTable = Repository.FirstOrDefault(u => u.ParentTableId == sysTableInfo.Id);
|
||||
|
||||
if (subTable == null) //如果子表不存在,则用单模版生成
|
||||
{
|
||||
if (sysTableInfo.IsDynamicHeader) //使用动态头部的模版
|
||||
{
|
||||
domainContent = FileHelper.ReadFile(@"Template\\SingleTable\\BuildAppWithDynamicHeader.html");
|
||||
}
|
||||
else
|
||||
{
|
||||
domainContent = FileHelper.ReadFile(@"Template\\SingleTable\\BuildApp.html");
|
||||
}
|
||||
|
||||
domainContent = domainContent
|
||||
.Replace("{ForeignKeyTemplate}", foreignTemplate);
|
||||
.Replace("{TableName}", sysTableInfo.TableName)
|
||||
.Replace("{ModuleCode}", sysTableInfo.ModuleCode)
|
||||
.Replace("{ModuleName}", sysTableInfo.ModuleName)
|
||||
.Replace("{ClassName}", sysTableInfo.ClassName)
|
||||
.Replace("{UpdateColumns}", updateColumns)
|
||||
.Replace("{InsertColumns}", insertColumns)
|
||||
.Replace("{StartName}", StratName);
|
||||
|
||||
//如果有外键(是一个子表)
|
||||
if (!string.IsNullOrEmpty(sysTableInfo.ForeignKey))
|
||||
{ //替换外键模版
|
||||
var foreignTemplate = $"objs = objs.Where(u => u.{sysTableInfo.ForeignKey} == request.{sysTableInfo.ForeignKey});";
|
||||
domainContent = domainContent
|
||||
.Replace("{ForeignKeyTemplate}", foreignTemplate);
|
||||
}
|
||||
else
|
||||
{
|
||||
domainContent = domainContent
|
||||
.Replace("{ForeignKeyTemplate}", "");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sysTableInfo.IsDynamicHeader) //使用动态头部的模版
|
||||
{
|
||||
domainContent = FileHelper.ReadFile(@"Template\\MultiTable\\BuildAppWithDynamicHeader.html");
|
||||
}
|
||||
else
|
||||
{
|
||||
domainContent = FileHelper.ReadFile(@"Template\\MultiTable\\BuildApp.html");
|
||||
}
|
||||
|
||||
domainContent = domainContent
|
||||
.Replace("{ForeignKeyTemplate}", "");
|
||||
.Replace("{TableName}", sysTableInfo.TableName)
|
||||
.Replace("{ModuleCode}", sysTableInfo.ModuleCode)
|
||||
.Replace("{ModuleName}", sysTableInfo.ModuleName)
|
||||
.Replace("{ClassName}", sysTableInfo.ClassName)
|
||||
.Replace("{SubForeignKey}", subTable.ForeignKey)
|
||||
.Replace("{SubClassName}", subTable.ClassName)
|
||||
.Replace("{SubModuleCode}", subTable.ModuleCode)
|
||||
.Replace("{UpdateColumns}", updateColumns)
|
||||
.Replace("{InsertColumns}", insertColumns)
|
||||
.Replace("{StartName}", StratName);
|
||||
|
||||
}
|
||||
|
||||
var primarykey = sysColumns.FirstOrDefault(u => u.IsKey);
|
||||
@@ -383,6 +462,15 @@ namespace OpenAuth.App
|
||||
attributeBuilder.Append("\r\n\r\n ");
|
||||
}
|
||||
|
||||
//查找是否存在子表的情况
|
||||
var subTable = Repository.FirstOrDefault(u => u.ParentTableId == sysTableInfo.Id);
|
||||
|
||||
if (subTable != null) //如果子表存在,需要加子表的请求参数
|
||||
{
|
||||
attributeBuilder.Append($" public List<AddOrUpdate{subTable.ClassName}Req> {subTable.ClassName}Reqs {{ get; set; }}");
|
||||
attributeBuilder.Append("\r\n\r\n ");
|
||||
}
|
||||
|
||||
domainContent = domainContent.Replace("{ClassName}", sysTableInfo.ClassName)
|
||||
.Replace("{AttributeList}", attributeBuilder.ToString());
|
||||
|
||||
@@ -537,9 +625,6 @@ namespace OpenAuth.App
|
||||
mapPath +
|
||||
$"\\OpenAuth.Repository\\Domain\\", tableInfo.ClassName + ".cs",
|
||||
domainContent);
|
||||
|
||||
string openAuthDBContextPath = mapPath + "\\OpenAuth.Repository\\OpenAuthDBContext.cs";
|
||||
FileHelper.RegxAddContentByParenthesis(openAuthDBContextPath, "public virtual DbSet<" + tableInfo.ClassName + "> " + tableInfo.TableName + "s { get; set; }");
|
||||
}
|
||||
|
||||
Dictionary<string, Type> PrimitiveTypes = new Dictionary<string, Type>()
|
||||
@@ -569,7 +654,8 @@ namespace OpenAuth.App
|
||||
{
|
||||
return "DateTime.Now";
|
||||
}
|
||||
else if (type == "bool"){
|
||||
else if (type == "bool")
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
return Activator.CreateInstance(t).ToString();
|
||||
|
||||
@@ -3,18 +3,15 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class CategoryApp : BaseStringApp<Category,OpenAuthDBContext>
|
||||
public class CategoryApp : SqlSugarBaseApp<Category>
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载列表
|
||||
@@ -40,7 +37,7 @@ namespace OpenAuth.App
|
||||
}
|
||||
|
||||
var result = new TableData();
|
||||
var objs = UnitWork.Find<Category>(null);
|
||||
var objs = SugarClient.Queryable<Category>();
|
||||
if (!string.IsNullOrEmpty(request.TypeId))
|
||||
{
|
||||
objs = objs.Where(u => u.TypeId == request.TypeId);
|
||||
@@ -51,11 +48,16 @@ namespace OpenAuth.App
|
||||
objs = objs.Where(u => u.Id.Contains(request.key) || u.Name.Contains(request.key));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
var propertyStr = string.Join(',', columnFields.Select(u =>u.ColumnName));
|
||||
result.columnFields = columnFields;
|
||||
result.data = objs.OrderBy(u => u.DtCode)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).Select($"new ({propertyStr})");
|
||||
.Take(request.limit).Select($"{propertyStr}").ToList();
|
||||
result.count = await objs.CountAsync();
|
||||
return result;
|
||||
}
|
||||
@@ -67,13 +69,13 @@ namespace OpenAuth.App
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.CreateUserId = user.Id;
|
||||
obj.CreateUserName = user.Name;
|
||||
await Repository.AddAsync(obj);
|
||||
await SugarClient.Insertable(obj).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdateCategoryReq obj)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
UnitWork.Update<Category>(u => u.Id == obj.Id, u => new Category
|
||||
Repository.Update(u => new Category
|
||||
{
|
||||
Name = obj.Name,
|
||||
Enable = obj.Enable,
|
||||
@@ -84,7 +86,7 @@ namespace OpenAuth.App
|
||||
UpdateUserId = user.Id,
|
||||
UpdateUserName = user.Name,
|
||||
SortNo = obj.SortNo
|
||||
});
|
||||
},u => u.Id == obj.Id);
|
||||
|
||||
}
|
||||
|
||||
@@ -95,10 +97,10 @@ namespace OpenAuth.App
|
||||
/// <returns></returns>
|
||||
public List<Category> LoadByTypeId(string typeId)
|
||||
{
|
||||
return Repository.Find(u => u.TypeId == typeId).ToList();
|
||||
return SugarClient.Queryable<Category>().Where(u => u.TypeId == typeId).ToList();
|
||||
}
|
||||
|
||||
public CategoryApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<Category,OpenAuthDBContext> repository,IAuth auth) : base(unitWork, repository, auth)
|
||||
public CategoryApp(ISqlSugarClient client, IAuth auth) : base(client, auth)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,16 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class DataPrivilegeRuleApp : BaseStringApp<DataPrivilegeRule,OpenAuthDBContext>
|
||||
public class DataPrivilegeRuleApp : SqlSugarBaseApp<DataPrivilegeRule>
|
||||
{
|
||||
private RevelanceManagerApp _revelanceApp;
|
||||
/// <summary>
|
||||
/// 加载列表
|
||||
/// </summary>
|
||||
@@ -34,36 +30,40 @@ namespace OpenAuth.App
|
||||
}
|
||||
|
||||
var result = new TableData();
|
||||
var objs = UnitWork.Find<DataPrivilegeRule>(null);
|
||||
var objs = SugarClient.Queryable<DataPrivilegeRule>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Id.Contains(request.key) || u.SourceCode.Contains(request.key) || u.Description.Contains(request.key));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
var propertyStr = string.Join(',', columnFields.Select(u => u.ColumnName));
|
||||
result.columnFields = columnFields;
|
||||
result.data = objs.OrderBy(u => u.Id)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).Select($"new ({propertyStr})");
|
||||
.Take(request.limit).Select($"{propertyStr}").ToList();
|
||||
result.count = await objs.CountAsync();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(AddOrUpdateDataPriviReq req)
|
||||
{
|
||||
if (Repository.Any(u => u.SourceCode == req.SourceCode))
|
||||
if (Repository.IsAny(u => u.SourceCode == req.SourceCode))
|
||||
{
|
||||
throw new Exception($"已经存在{req.SourceCode}的数据规则,如果想调整规制请直接修改");
|
||||
}
|
||||
var obj = req.MapTo<DataPrivilegeRule>();
|
||||
obj.CreateUserId = _auth.GetCurrentUser().User.Id;
|
||||
obj.CreateTime = DateTime.Now;
|
||||
Repository.Add(obj);
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdateDataPriviReq obj)
|
||||
{
|
||||
UnitWork.Update<DataPrivilegeRule>(u => u.Id == obj.Id, u => new DataPrivilegeRule
|
||||
Repository.Update(u => new DataPrivilegeRule
|
||||
{
|
||||
SortNo = obj.SortNo,
|
||||
SourceCode = obj.SourceCode,
|
||||
@@ -73,24 +73,22 @@ namespace OpenAuth.App
|
||||
PrivilegeRules = obj.PrivilegeRules,
|
||||
Enable = obj.Enable
|
||||
//todo:要修改的字段赋值
|
||||
});
|
||||
},u => u.Id == obj.Id);
|
||||
|
||||
}
|
||||
|
||||
public DataPrivilegeRuleApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<DataPrivilegeRule,OpenAuthDBContext> repository,
|
||||
RevelanceManagerApp app, IAuth auth) : base(unitWork, repository, auth)
|
||||
public DataPrivilegeRuleApp(ISqlSugarClient client, IAuth auth) : base(client, auth)
|
||||
{
|
||||
_revelanceApp = app;
|
||||
}
|
||||
|
||||
public DataPrivilegeRule GetByModuleName(string moduleName)
|
||||
{
|
||||
return Repository.FirstOrDefault(u=>u.SourceCode == moduleName);
|
||||
return Repository.GetFirst(u=>u.SourceCode == moduleName);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Repository.Delete(u =>true);
|
||||
SugarClient.Deleteable<DataPrivilegeRule>(u => true).ExecuteCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,11 @@ namespace OpenAuth.App
|
||||
/// <returns></returns>
|
||||
public IList<SysTableColumn> GetDbTableStructure(string tableName)
|
||||
{
|
||||
if(string.IsNullOrEmpty(tableName))
|
||||
{
|
||||
return new List<SysTableColumn>();
|
||||
}
|
||||
|
||||
//如果是空,说明没有通过ProcessExternalDb设置过,直接从本地读取
|
||||
if(string.IsNullOrEmpty(_dbTypeStr)){
|
||||
_dbTypeStr = _appConfiguration.Value.DbTypes[_httpContextAccessor.GetTenantId()];
|
||||
@@ -414,6 +419,22 @@ namespace OpenAuth.App
|
||||
return new List<SysTableColumn>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过表名和外部数据源ID获取表结构
|
||||
/// </summary>
|
||||
/// <param name="tableName">表名</param>
|
||||
/// <param name="externalDataSourceId">外部数据源ID</param>
|
||||
/// <returns></returns>
|
||||
public IList<SysTableColumn> GetDbTableStructure(string tableName, string externalDataSourceId){
|
||||
var connection = SugarClient.Queryable<ExternalDataSource>().First(u => u.Id == externalDataSourceId);
|
||||
if (connection != null)
|
||||
{
|
||||
var dbType = connection.Dbtype;
|
||||
SetConnection(connection.Connectionstring, dbType);
|
||||
}
|
||||
return GetDbTableStructure(tableName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理外部数据库
|
||||
/// </summary>
|
||||
|
||||
@@ -34,27 +34,156 @@ namespace OpenAuth.App.Flow
|
||||
bool result = true;
|
||||
foreach (var compare in Compares)
|
||||
{
|
||||
|
||||
bool isDecimal = decimal.TryParse(compare.Value, out decimal value);
|
||||
var fieldVal = frmDataJson.GetValue(compare.FieldName.ToLower()).ToString();
|
||||
|
||||
if (isDecimal) //如果是数字或小数
|
||||
object objVal = frmDataJson.GetValue(compare.FieldName.ToLower());
|
||||
string fieldVal = null;
|
||||
List<string> fieldVals = new List<string>();
|
||||
if(compare.FieldType == "checkbox")
|
||||
{
|
||||
decimal frmvalue = 0;
|
||||
if (fieldVal.Contains("["))//表单中的数据包含[],为数组,一般是checkbox的值
|
||||
if (objVal is JArray jArray)
|
||||
{
|
||||
var tempvals = JsonHelper.Instance.Deserialize<List<decimal>>(fieldVal);
|
||||
frmvalue = tempvals.Max();
|
||||
var tempvals = jArray.ToObject<List<string>>();
|
||||
if (tempvals != null && tempvals.Any())
|
||||
{
|
||||
fieldVals.AddRange(tempvals);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldVal = objVal?.ToString();
|
||||
}
|
||||
|
||||
|
||||
// 判断是否为空值操作符
|
||||
if (compare.Operation == DataCompare.IsNull || compare.Operation == DataCompare.IsNotNull)
|
||||
{
|
||||
var fieldExists = objVal != null;
|
||||
var fieldEmpty = !fieldExists || string.IsNullOrWhiteSpace(objVal.ToString());
|
||||
|
||||
if (compare.Operation == DataCompare.IsNull)
|
||||
{
|
||||
result &= fieldEmpty;
|
||||
}
|
||||
else
|
||||
{
|
||||
frmvalue = decimal.Parse(fieldVal); //表单中填写的值
|
||||
result &= !fieldEmpty;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 确保字段存在且不为空
|
||||
if (objVal == null)
|
||||
{
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// checkbox类型特殊处理
|
||||
if (compare.FieldType == "checkbox")
|
||||
{
|
||||
// 针对checkbox的IN和NOT IN操作
|
||||
if (compare.Operation == DataCompare.In || compare.Operation == DataCompare.NotIn)
|
||||
{
|
||||
if (compare.ValueList == null || compare.ValueList.Length == 0)
|
||||
{
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否有交集
|
||||
bool hasIntersection = fieldVals.Any(v => compare.ValueList.Contains(v));
|
||||
result &= compare.Operation == DataCompare.In ? hasIntersection : !hasIntersection;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 针对checkbox的相等和不等操作
|
||||
if (compare.Operation == DataCompare.Equal)
|
||||
{
|
||||
result &= fieldVals.Contains(compare.Value);
|
||||
continue;
|
||||
}
|
||||
else if (compare.Operation == DataCompare.NotEqual)
|
||||
{
|
||||
result &= !fieldVals.Contains(compare.Value);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 其他操作不适用于checkbox,默认为false
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 以下是非checkbox类型的处理逻辑
|
||||
// 范围操作符处理
|
||||
if (compare.Operation == DataCompare.In || compare.Operation == DataCompare.NotIn)
|
||||
{
|
||||
if (compare.ValueList == null || compare.ValueList.Length == 0)
|
||||
{
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool inList = compare.ValueList.Contains(fieldVal);
|
||||
result &= compare.Operation == DataCompare.In ? inList : !inList;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Between操作符处理
|
||||
if (compare.Operation == DataCompare.Between)
|
||||
{
|
||||
if (compare.ValueRange == null || compare.ValueRange.Length != 2)
|
||||
{
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isDecimal = decimal.TryParse(fieldVal, out decimal fieldDecimal);
|
||||
if (isDecimal)
|
||||
{
|
||||
decimal min = decimal.Parse(compare.ValueRange[0]);
|
||||
decimal max = decimal.Parse(compare.ValueRange[1]);
|
||||
result &= fieldDecimal >= min && fieldDecimal <= max;
|
||||
}
|
||||
else
|
||||
{
|
||||
string min = compare.ValueRange[0];
|
||||
string max = compare.ValueRange[1];
|
||||
result &= string.Compare(fieldVal, min, false) >= 0 && string.Compare(fieldVal, max, false) <= 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 文本特殊操作符处理
|
||||
if (compare.Operation == DataCompare.Like || compare.Operation == DataCompare.NotLike ||
|
||||
compare.Operation == DataCompare.StartWith || compare.Operation == DataCompare.EndWith)
|
||||
{
|
||||
switch (compare.Operation)
|
||||
{
|
||||
case DataCompare.Like:
|
||||
result &= fieldVal.Contains(compare.Value);
|
||||
break;
|
||||
case DataCompare.NotLike:
|
||||
result &= !fieldVal.Contains(compare.Value);
|
||||
break;
|
||||
case DataCompare.StartWith:
|
||||
result &= fieldVal.StartsWith(compare.Value);
|
||||
break;
|
||||
case DataCompare.EndWith:
|
||||
result &= fieldVal.EndsWith(compare.Value);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isDecimalValue = decimal.TryParse(compare.Value, out decimal value);
|
||||
|
||||
if (isDecimalValue) //如果是数字或小数
|
||||
{
|
||||
decimal frmvalue = decimal.Parse(fieldVal);
|
||||
switch (compare.Operation)
|
||||
{
|
||||
case DataCompare.Equal:
|
||||
result &= compare.Value == fieldVal;
|
||||
result &= frmvalue == value;
|
||||
break;
|
||||
case DataCompare.Larger:
|
||||
result &= frmvalue > value;
|
||||
@@ -68,47 +197,35 @@ namespace OpenAuth.App.Flow
|
||||
case DataCompare.LessEqual:
|
||||
result &= frmvalue <= value;
|
||||
break;
|
||||
case DataCompare.NotEqual:
|
||||
case DataCompare.NotEqual:
|
||||
result &= frmvalue != value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else //如果只是字符串,只判断相等
|
||||
else //如果只是字符串
|
||||
{
|
||||
if (fieldVal.Contains("["))//表单中的数据包含[],为数组,一般是checkbox的值
|
||||
switch (compare.Operation)
|
||||
{
|
||||
var tempvals = JsonHelper.Instance.Deserialize<List<string>>(fieldVal);
|
||||
result &= tempvals.Contains(compare.Value);
|
||||
case DataCompare.Equal:
|
||||
result &= compare.Value == fieldVal;
|
||||
break;
|
||||
case DataCompare.Larger:
|
||||
result &= string.Compare(fieldVal, compare.Value, false) > 0;
|
||||
break;
|
||||
case DataCompare.Less:
|
||||
result &= string.Compare(fieldVal, compare.Value, false) < 0;
|
||||
break;
|
||||
case DataCompare.LargerEqual:
|
||||
result &= string.Compare(fieldVal, compare.Value, false) >= 0;
|
||||
break;
|
||||
case DataCompare.LessEqual:
|
||||
result &= string.Compare(fieldVal, compare.Value, false) <= 0;
|
||||
break;
|
||||
case DataCompare.NotEqual:
|
||||
result &= compare.Value != fieldVal;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (compare.Operation)
|
||||
{
|
||||
case DataCompare.Equal:
|
||||
result &= compare.Value == fieldVal;
|
||||
break;
|
||||
case DataCompare.Larger:
|
||||
result &= string.Compare(compare.Value, fieldVal, false) > 0;
|
||||
break;
|
||||
case DataCompare.Less:
|
||||
result &= string.Compare(compare.Value, fieldVal, false) < 0;
|
||||
break;
|
||||
case DataCompare.LargerEqual:
|
||||
result &= string.Compare(compare.Value, fieldVal, false) >= 0;
|
||||
break;
|
||||
case DataCompare.LessEqual:
|
||||
result &= string.Compare(compare.Value, fieldVal, false) <= 0;
|
||||
break;
|
||||
case DataCompare.NotEqual:
|
||||
result &= compare.Value != fieldVal;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -120,6 +237,7 @@ namespace OpenAuth.App.Flow
|
||||
/// </summary>
|
||||
public class DataCompare
|
||||
{
|
||||
// 基本比较操作符
|
||||
public const string Larger = ">";
|
||||
public const string Less = "<";
|
||||
public const string LargerEqual = ">=";
|
||||
@@ -127,6 +245,21 @@ namespace OpenAuth.App.Flow
|
||||
public const string NotEqual = "!=";
|
||||
public const string Equal = "=";
|
||||
|
||||
// 文本操作符
|
||||
public const string Like = "LIKE";
|
||||
public const string NotLike = "NOT LIKE";
|
||||
public const string StartWith = "START_WITH";
|
||||
public const string EndWith = "END_WITH";
|
||||
|
||||
// 范围操作符
|
||||
public const string In = "IN";
|
||||
public const string NotIn = "NOT IN";
|
||||
public const string Between = "BETWEEN";
|
||||
|
||||
// 空值操作符
|
||||
public const string IsNull = "IS NULL";
|
||||
public const string IsNotNull = "IS NOT NULL";
|
||||
|
||||
/// <summary>操作类型比如大于/等于/小于</summary>
|
||||
public string Operation { get; set; }
|
||||
|
||||
@@ -138,5 +271,11 @@ namespace OpenAuth.App.Flow
|
||||
|
||||
/// <summary>比较的值</summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary> 值范围BETWEEN </summary>
|
||||
public string[] ValueRange { get; set; }
|
||||
|
||||
/// <summary> IN 和 NOT IN 操作符 </summary>
|
||||
public string[] ValueList { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +124,14 @@ namespace OpenAuth.App.Flow
|
||||
|
||||
if (FrmData == "" || FrmData == "{}") return lines[0].to;
|
||||
|
||||
FrmData = FrmData.ToLower(); //统一转小写
|
||||
var frmDataJson = FrmData.ToJObject(); //获取数据内容
|
||||
// 将frmDataJson中的所有key转小写
|
||||
var properties = frmDataJson.Properties().ToList();
|
||||
foreach (var property in properties)
|
||||
{
|
||||
frmDataJson[property.Name.ToLower()] = property.Value;
|
||||
frmDataJson.Remove(property.Name);
|
||||
}
|
||||
|
||||
foreach (var l in lines)
|
||||
{
|
||||
@@ -375,7 +381,6 @@ namespace OpenAuth.App.Flow
|
||||
flowInstance.ActivityName = Nodes[rejectNode].name;
|
||||
flowInstance.MakerList =
|
||||
GetNodeMarkers(Nodes[rejectNode], flowInstance.CreateUserId);
|
||||
SaveTransitionHis();
|
||||
}
|
||||
|
||||
flowInstance.SchemeContent = JsonHelper.Instance.Serialize(ToSchemeObj());
|
||||
@@ -405,8 +410,6 @@ namespace OpenAuth.App.Flow
|
||||
flowInstance.ActivityName = Nodes[startNodeId].name;
|
||||
flowInstance.MakerList = GetNodeMarkers(Nodes[startNodeId], flowInstance.CreateUserId);
|
||||
|
||||
SaveTransitionHis();
|
||||
|
||||
var sugarClient = AutofacContainerModule.GetService<ISqlSugarClient>();
|
||||
sugarClient.Updateable(flowInstance).ExecuteCommand();
|
||||
|
||||
@@ -478,32 +481,6 @@ namespace OpenAuth.App.Flow
|
||||
SaveOperationHis(user.Id, user.Name, opHis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存本次扭转记录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void SaveTransitionHis()
|
||||
{
|
||||
var user = AutofacContainerModule.GetService<IAuth>().GetCurrentUser().User;
|
||||
var SugarClient = AutofacContainerModule.GetService<ISqlSugarClient>();
|
||||
var transitionHistory = new FlowInstanceTransitionHistory
|
||||
{
|
||||
InstanceId = flowInstanceId,
|
||||
CreateUserId = user.Id,
|
||||
CreateUserName = user.Name,
|
||||
FromNodeId = currentNodeId,
|
||||
FromNodeName = currentNode.name,
|
||||
FromNodeType = GetCurrentNodeType(),
|
||||
ToNodeId = nextNodeId,
|
||||
ToNodeName = nextNode?.name,
|
||||
ToNodeType = GetNextNodeType(),
|
||||
IsFinish = IsFinish() ? FlowInstanceStatus.Finished : FlowInstanceStatus.Running,
|
||||
TransitionSate = 0
|
||||
};
|
||||
|
||||
SugarClient.Insertable(transitionHistory).ExecuteCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通知三方系统,节点执行情况
|
||||
/// </summary>
|
||||
@@ -580,12 +557,10 @@ namespace OpenAuth.App.Flow
|
||||
currentNode.setInfo.Description = "";
|
||||
currentNode.setInfo.TagedTime = "";
|
||||
|
||||
//删除当前节点的扭转记录
|
||||
//删除当前节点的审批记录(只删除最新的一条)
|
||||
var user = AutofacContainerModule.GetService<IAuth>().GetCurrentUser().User;
|
||||
var SugarClient = AutofacContainerModule.GetService<ISqlSugarClient>();
|
||||
SugarClient.Deleteable<FlowInstanceTransitionHistory>().Where(u => u.InstanceId == flowInstanceId && u.CreateUserId == user.Id && u.FromNodeId == currentNodeId).ExecuteCommand();
|
||||
|
||||
//删除当前节点的审批记录(只删除最新的一条)
|
||||
|
||||
var latestRecord = SugarClient.Queryable<FlowInstanceOperationHistory>()
|
||||
.Where(u => u.InstanceId == flowInstanceId && u.CreateUserId == user.Id)
|
||||
.OrderByDescending(u => u.CreateDate)
|
||||
|
||||
@@ -219,7 +219,6 @@ namespace OpenAuth.App
|
||||
|
||||
wfruntime.SaveOperationHis($"【创建】{user.User.Name}创建了流程实例【{addFlowInstanceReq.CustomName}】");
|
||||
|
||||
wfruntime.SaveTransitionHis();
|
||||
SugarClient.Ado.CommitTran();
|
||||
return flowInstance.Id;
|
||||
}
|
||||
@@ -408,14 +407,11 @@ namespace OpenAuth.App
|
||||
: FlowInstanceStatus.Running;
|
||||
flowInstance.MakerList =
|
||||
wfruntime.IsFinish() ? "" : wfruntime.GetNextMakers();
|
||||
|
||||
wfruntime.SaveTransitionHis();
|
||||
}
|
||||
else
|
||||
{
|
||||
//网关过程中,需要更新用户
|
||||
flowInstance.MakerList = wfruntime.GetForkNodeMakers(wfruntime.currentNodeId);
|
||||
wfruntime.SaveTransitionHis();
|
||||
}
|
||||
|
||||
flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.ToSchemeObj());
|
||||
@@ -898,7 +894,6 @@ namespace OpenAuth.App
|
||||
#endregion 根据运行实例改变当前节点状态
|
||||
|
||||
wfruntime.SaveOperationHis($"【启动】由用户{user.User.Name}启动");
|
||||
wfruntime.SaveTransitionHis();
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,21 +8,18 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenAuth.App.Extensions;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Jobs;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
using Quartz;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统定时任务管理
|
||||
/// </summary>
|
||||
public class OpenJobApp : BaseStringApp<OpenJob, OpenAuthDBContext>
|
||||
public class OpenJobApp : SqlSugarBaseApp<OpenJob>
|
||||
{
|
||||
private SysLogApp _sysLogApp;
|
||||
private IScheduler _scheduler;
|
||||
@@ -34,7 +31,7 @@ namespace OpenAuth.App
|
||||
public async Task<TableData> Load(QueryOpenJobListReq request)
|
||||
{
|
||||
var result = new TableData();
|
||||
var objs = Repository.Find(null);
|
||||
var objs = SugarClient.Queryable<OpenJob>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Id.Contains(request.key));
|
||||
@@ -54,7 +51,7 @@ namespace OpenAuth.App
|
||||
/// <returns></returns>
|
||||
public async Task StartAll()
|
||||
{
|
||||
var jobs = await Repository.Find(u => u.Status == (int) JobStatus.Running).ToListAsync();
|
||||
var jobs = await SugarClient.Queryable<OpenJob>().Where(u => u.Status == (int) JobStatus.Running).ToListAsync();
|
||||
foreach (var job in jobs)
|
||||
{
|
||||
job.Start(_scheduler);
|
||||
@@ -69,13 +66,13 @@ namespace OpenAuth.App
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.CreateUserId = user.Id;
|
||||
obj.CreateUserName = user.Name;
|
||||
Repository.Add(obj);
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdateOpenJobReq obj)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
UnitWork.Update<OpenJob>(u => u.Id == obj.Id, u => new OpenJob
|
||||
Repository.Update(u => new OpenJob
|
||||
{
|
||||
JobName = obj.JobName,
|
||||
JobType = obj.JobType,
|
||||
@@ -87,7 +84,7 @@ namespace OpenAuth.App
|
||||
UpdateTime = DateTime.Now,
|
||||
UpdateUserId = user.Id,
|
||||
UpdateUserName = user.Name
|
||||
});
|
||||
},u => u.Id == obj.Id);
|
||||
}
|
||||
|
||||
#region 定时任务运行相关操作
|
||||
@@ -109,7 +106,7 @@ namespace OpenAuth.App
|
||||
|
||||
public void ChangeJobStatus(ChangeJobStatusReq req)
|
||||
{
|
||||
var job = Repository.FirstOrDefault(u => u.Id == req.Id);
|
||||
var job = Repository.GetFirst(u => u.Id == req.Id);
|
||||
if (job == null)
|
||||
{
|
||||
throw new Exception("任务不存在");
|
||||
@@ -140,7 +137,7 @@ namespace OpenAuth.App
|
||||
/// <param name="jobId"></param>
|
||||
public void RecordRun(string jobId)
|
||||
{
|
||||
var job = Repository.FirstOrDefault(u => u.Id == jobId);
|
||||
var job = Repository.GetFirst(u => u.Id == jobId);
|
||||
if (job == null)
|
||||
{
|
||||
_sysLogApp.Add(new SysLog
|
||||
@@ -170,9 +167,7 @@ namespace OpenAuth.App
|
||||
#endregion
|
||||
|
||||
|
||||
public OpenJobApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<OpenJob, OpenAuthDBContext> repository,
|
||||
IAuth auth, SysLogApp sysLogApp, IScheduler scheduler, ILogger<OpenJobApp> logger) : base(unitWork,
|
||||
repository, auth)
|
||||
public OpenJobApp(ISqlSugarClient client, IAuth auth, SysLogApp sysLogApp, IScheduler scheduler, ILogger<OpenJobApp> logger) : base(client, auth)
|
||||
{
|
||||
_sysLogApp = sysLogApp;
|
||||
_scheduler = scheduler;
|
||||
|
||||
@@ -96,6 +96,11 @@ namespace OpenAuth.App
|
||||
resources = resources.Where(u => u.AppId == request.appId);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
resources = resources.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
var columnnames = columnFields.Select(u => u.ColumnName);
|
||||
|
||||
var propertyStr = string.Join(',', columnnames);
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace OpenAuth.App
|
||||
objs = objs.Where(u => u.Name.Contains(request.key));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
var columnnames = columnFields.Select(u => u.ColumnName);
|
||||
|
||||
var propertyStr = string.Join(',', columnnames);
|
||||
|
||||
131
OpenAuth.App/Test/TestWmsInbound.cs
Normal file
131
OpenAuth.App/Test/TestWmsInbound.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Cache;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.SSO;
|
||||
using System.Collections.Generic;
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace OpenAuth.App.Test
|
||||
{
|
||||
public class TestWmsInbound : TestBase
|
||||
{
|
||||
public override ServiceCollection GetService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
var cachemock = new Mock<ICacheContext>();
|
||||
cachemock.Setup(x => x.Get<UserAuthSession>("tokentest"))
|
||||
.Returns(new UserAuthSession { Account = Define.SYSTEM_USERNAME });
|
||||
services.AddScoped(x => cachemock.Object);
|
||||
|
||||
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
|
||||
httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME])
|
||||
.Returns("tokentest");
|
||||
|
||||
services.AddScoped(x => httpContextAccessorMock.Object);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Add()
|
||||
{
|
||||
var app = _autofacServiceProvider.GetService<WmsInboundOrderTblApp>();
|
||||
var options = new IdGeneratorOptions()
|
||||
{
|
||||
Method = 1,
|
||||
WorkerId = 1
|
||||
};
|
||||
YitIdHelper.SetIdGenerator(options);
|
||||
|
||||
//添加
|
||||
var addReq = new AddOrUpdateWmsInboundOrderTblReq()
|
||||
{
|
||||
Id = "673870840217669",
|
||||
ExternalNo = "123456",
|
||||
ExternalType = "1",
|
||||
Status = 1,
|
||||
OrderType = "1",
|
||||
StockId = "123456",
|
||||
OwnerId = "123456",
|
||||
Remark = "123456",
|
||||
|
||||
WmsInboundOrderDtblReqs = new List<AddOrUpdateWmsInboundOrderDtblReq>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
GoodsId = "123456",
|
||||
GoodsBatch = "123456",
|
||||
QualityFlg = "1",
|
||||
OrderNum = 1,
|
||||
InNum = 1,
|
||||
LeaveNum = 1,
|
||||
HoldNum = 1,
|
||||
OwnerId = "123456",
|
||||
}
|
||||
}
|
||||
};
|
||||
app.Add(addReq);
|
||||
|
||||
//异步测试,延长主线程,防止程序退出
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Update()
|
||||
{
|
||||
var app = _autofacServiceProvider.GetService<WmsInboundOrderTblApp>();
|
||||
var updateReq = new AddOrUpdateWmsInboundOrderTblReq()
|
||||
{
|
||||
Id = "673870840217669",
|
||||
ExternalNo = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
ExternalType = "1",
|
||||
Status = 1,
|
||||
OrderType = "1",
|
||||
StockId = "123456",
|
||||
OwnerId = "123456",
|
||||
Remark = "123456",
|
||||
WmsInboundOrderDtblReqs = new List<AddOrUpdateWmsInboundOrderDtblReq>()
|
||||
{
|
||||
new()
|
||||
{ //这个用来测试更新
|
||||
Id = "52c69eb9-e28c-47d9-8888-3e5c1090716b",
|
||||
OrderId = "673870840217669",
|
||||
GoodsId = "123456",
|
||||
OwnerId = "123456",
|
||||
GoodsBatch = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
QualityFlg = "1",
|
||||
OrderNum = 1,
|
||||
InNum = 1,
|
||||
LeaveNum = 1,
|
||||
HoldNum = 1
|
||||
},
|
||||
new()
|
||||
{ //这个用来测试添加
|
||||
OrderId = "673870840217669",
|
||||
GoodsId = "123456",
|
||||
OwnerId = "123456",
|
||||
GoodsBatch = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
QualityFlg = "1",
|
||||
OrderNum = 1,
|
||||
InNum = 1,
|
||||
LeaveNum = 1,
|
||||
HoldNum = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
app.Update(updateReq);
|
||||
|
||||
//异步测试,延长主线程,防止程序退出
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,7 @@ using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class WmsInboundOrderDtblApp : SqlSugarBaseApp<WmsInboundOrderDtbl>
|
||||
@@ -18,13 +16,11 @@ namespace OpenAuth.App
|
||||
/// </summary>
|
||||
public async Task<TableData> Load(QueryWmsInboundOrderDtblListReq request)
|
||||
{
|
||||
|
||||
var loginContext = _auth.GetCurrentUser();
|
||||
if (loginContext == null)
|
||||
{
|
||||
throw new CommonException("登录已过期", Define.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
var properties = loginContext.GetTableColumns("WmsInboundOrderDtbl");
|
||||
if (properties == null || properties.Count == 0)
|
||||
{
|
||||
@@ -36,12 +32,10 @@ namespace OpenAuth.App
|
||||
{
|
||||
objs = objs.Where(u => u.OrderId == request.InboundOrderId);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.GoodsId.Contains(request.key));
|
||||
}
|
||||
|
||||
var propertyStr = string.Join(',', properties.Select(u => u.ColumnName));
|
||||
result.columnFields = properties;
|
||||
result.data = objs.OrderBy(u => u.Id)
|
||||
@@ -50,17 +44,9 @@ namespace OpenAuth.App
|
||||
result.count = await objs.CountAsync();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(AddOrUpdateWmsInboundOrderDtblReq req)
|
||||
public void Add(AddOrUpdateWmsInboundOrderDtblReq request)
|
||||
{
|
||||
SugarClient.Ado.BeginTran();
|
||||
AddNoSave(req);
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
|
||||
public void AddNoSave(AddOrUpdateWmsInboundOrderDtblReq req)
|
||||
{
|
||||
var obj = req.MapTo<WmsInboundOrderDtbl>();
|
||||
var obj = request.MapTo<WmsInboundOrderDtbl>();
|
||||
//todo:补充或调整自己需要的字段
|
||||
obj.CreateTime = DateTime.Now;
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
@@ -69,36 +55,33 @@ namespace OpenAuth.App
|
||||
obj.CreateUserName = user.Name;
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdateWmsInboundOrderDtblReq obj)
|
||||
public void Update(AddOrUpdateWmsInboundOrderDtblReq request)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
Repository.Update(u => new WmsInboundOrderDtbl
|
||||
{
|
||||
Price = obj.Price,
|
||||
PriceNoTax = obj.PriceNoTax,
|
||||
InStockStatus = obj.InStockStatus,
|
||||
AsnStatus = obj.AsnStatus,
|
||||
GoodsId = obj.GoodsId,
|
||||
GoodsBatch = obj.GoodsBatch,
|
||||
QualityFlg = obj.QualityFlg,
|
||||
OrderNum = obj.OrderNum,
|
||||
InNum = obj.InNum,
|
||||
LeaveNum = obj.LeaveNum,
|
||||
HoldNum = obj.HoldNum,
|
||||
ProdDate = obj.ProdDate,
|
||||
ExpireDate = obj.ExpireDate,
|
||||
TaxRate = obj.TaxRate,
|
||||
OwnerId = obj.OwnerId,
|
||||
Remark = obj.Remark,
|
||||
Price = request.Price,
|
||||
PriceNoTax = request.PriceNoTax,
|
||||
InStockStatus = request.InStockStatus,
|
||||
AsnStatus = request.AsnStatus,
|
||||
GoodsId = request.GoodsId,
|
||||
GoodsBatch = request.GoodsBatch,
|
||||
QualityFlg = request.QualityFlg,
|
||||
OrderNum = request.OrderNum,
|
||||
InNum = request.InNum,
|
||||
LeaveNum = request.LeaveNum,
|
||||
HoldNum = request.HoldNum,
|
||||
ProdDate = request.ProdDate,
|
||||
ExpireDate = request.ExpireDate,
|
||||
TaxRate = request.TaxRate,
|
||||
OwnerId = request.OwnerId,
|
||||
Remark = request.Remark,
|
||||
UpdateTime = DateTime.Now,
|
||||
UpdateUserId = user.Id,
|
||||
UpdateUserName = user.Name
|
||||
//todo:补充或调整自己需要的字段
|
||||
}, u => u.Id == obj.Id);
|
||||
|
||||
}, u => u.Id == request.Id);
|
||||
}
|
||||
|
||||
public WmsInboundOrderDtblApp(ISqlSugarClient client, IAuth auth) : base(client, auth)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -7,14 +7,11 @@ using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using SqlSugar;
|
||||
|
||||
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class WmsInboundOrderTblApp : SqlSugarBaseApp<WmsInboundOrderTbl>
|
||||
{
|
||||
private WmsInboundOrderDtblApp _wmsInboundOrderDtblApp;
|
||||
|
||||
/// <summary>
|
||||
/// 加载列表
|
||||
/// </summary>
|
||||
@@ -25,29 +22,22 @@ namespace OpenAuth.App
|
||||
{
|
||||
throw new CommonException("登录已过期", Define.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
|
||||
var columns = loginContext.GetTableColumns("WmsInboundOrderTbl");
|
||||
if (columns == null || columns.Count == 0)
|
||||
{
|
||||
throw new Exception("请在代码生成界面配置WmsInboundOrderTbl表的字段属性");
|
||||
}
|
||||
|
||||
var result = new TableData();
|
||||
|
||||
result.columnFields = columns;
|
||||
|
||||
var objs = GetDataPrivilege("u");
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Id.Contains(request.key));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
var propertyStr = string.Join(',', columns.Select(u => u.ColumnName));
|
||||
result.data = objs.OrderBy(u => u.Id)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
@@ -55,88 +45,81 @@ namespace OpenAuth.App
|
||||
result.count = await objs.CountAsync();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(AddOrUpdateWmsInboundOrderTblReq req)
|
||||
public void Add(AddOrUpdateWmsInboundOrderTblReq request)
|
||||
{
|
||||
var obj = req.MapTo<WmsInboundOrderTbl>();
|
||||
var obj = request.MapTo<WmsInboundOrderTbl>();
|
||||
//todo:补充或调整自己需要的字段
|
||||
obj.CreateTime = DateTime.Now;
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.CreateUserId = user.Id;
|
||||
obj.CreateUserName = user.Name;
|
||||
SugarClient.Ado.BeginTran();
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
if (req.WmsInboundOrderDtblReqs != null && req.WmsInboundOrderDtblReqs.Any())
|
||||
if(obj.KeyIsNull()) //如果主键为空,则生成默认值
|
||||
{
|
||||
foreach (var detail in req.WmsInboundOrderDtblReqs)
|
||||
{
|
||||
detail.OrderId = obj.Id;
|
||||
_wmsInboundOrderDtblApp.AddNoSave(detail);
|
||||
}
|
||||
obj.GenerateDefaultKeyVal();
|
||||
}
|
||||
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdateWmsInboundOrderTblReq obj)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
|
||||
SugarClient.Ado.BeginTran();
|
||||
|
||||
|
||||
if (obj.WmsInboundOrderDtblReqs != null && obj.WmsInboundOrderDtblReqs.Any())
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
if (request.WmsInboundOrderDtblReqs != null && request.WmsInboundOrderDtblReqs.Any())
|
||||
{
|
||||
//id为空的添加
|
||||
foreach (var detail in obj.WmsInboundOrderDtblReqs.Where(u => string.IsNullOrEmpty(u.Id)))
|
||||
foreach (var detail in request.WmsInboundOrderDtblReqs)
|
||||
{
|
||||
detail.OrderId = obj.Id;
|
||||
_wmsInboundOrderDtblApp.AddNoSave(detail);
|
||||
_wmsInboundOrderDtblApp.Add(detail);
|
||||
}
|
||||
|
||||
//id比数据库少的,删除
|
||||
var containids = obj.WmsInboundOrderDtblReqs.Select(u => u.Id)
|
||||
}
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
public void Update(AddOrUpdateWmsInboundOrderTblReq request)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
SugarClient.Ado.BeginTran();
|
||||
if (request.WmsInboundOrderDtblReqs != null && request.WmsInboundOrderDtblReqs.Any())
|
||||
{
|
||||
//请求的id不在数据库的记录,需要删除
|
||||
var containids = request.WmsInboundOrderDtblReqs.Select(u => u.Id)
|
||||
.Where(u => !string.IsNullOrEmpty(u)).ToList();
|
||||
if (containids.Any())
|
||||
{
|
||||
SugarClient.Deleteable<WmsInboundOrderDtbl>(u => (!containids.Contains(u.Id)) && u.OrderId == obj.Id).ExecuteCommand();
|
||||
SugarClient.Deleteable<WmsInboundOrderDtbl>(u => (!containids.Contains(u.Id)) && u.OrderId == request.Id).ExecuteCommand();
|
||||
}
|
||||
//id为空的添加
|
||||
foreach (var detail in request.WmsInboundOrderDtblReqs.Where(u => string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
detail.OrderId = request.Id;
|
||||
_wmsInboundOrderDtblApp.Add(detail);
|
||||
}
|
||||
|
||||
//更新id相同的
|
||||
foreach (var detail in obj.WmsInboundOrderDtblReqs.Where(u => !string.IsNullOrEmpty(u.Id)))
|
||||
foreach (var detail in request.WmsInboundOrderDtblReqs.Where(u => !string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
_wmsInboundOrderDtblApp.Update(detail);
|
||||
}
|
||||
}
|
||||
|
||||
Repository.Update(u => new WmsInboundOrderTbl
|
||||
{
|
||||
ExternalNo = obj.ExternalNo,
|
||||
ExternalType = obj.ExternalType,
|
||||
Status = obj.Status,
|
||||
OrderType = obj.OrderType,
|
||||
GoodsType = obj.GoodsType,
|
||||
PurchaseNo = obj.PurchaseNo,
|
||||
StockId = obj.StockId,
|
||||
OwnerId = obj.OwnerId,
|
||||
ShipperId = obj.ShipperId,
|
||||
SupplierId = obj.SupplierId,
|
||||
ScheduledInboundTime = obj.ScheduledInboundTime,
|
||||
Remark = obj.Remark,
|
||||
Enable = obj.Enable,
|
||||
TransferType = obj.TransferType,
|
||||
InBondedArea = obj.InBondedArea,
|
||||
ReturnBoxNum = obj.ReturnBoxNum,
|
||||
ExternalNo = request.ExternalNo,
|
||||
ExternalType = request.ExternalType,
|
||||
Status = request.Status,
|
||||
OrderType = request.OrderType,
|
||||
GoodsType = request.GoodsType,
|
||||
PurchaseNo = request.PurchaseNo,
|
||||
StockId = request.StockId,
|
||||
OwnerId = request.OwnerId,
|
||||
ShipperId = request.ShipperId,
|
||||
SupplierId = request.SupplierId,
|
||||
ScheduledInboundTime = request.ScheduledInboundTime,
|
||||
Remark = request.Remark,
|
||||
Enable = request.Enable,
|
||||
TransferType = request.TransferType,
|
||||
InBondedArea = request.InBondedArea,
|
||||
ReturnBoxNum = request.ReturnBoxNum,
|
||||
UpdateTime = DateTime.Now,
|
||||
UpdateUserId = user.Id,
|
||||
UpdateUserName = user.Name
|
||||
//todo:补充或调整自己需要的字段
|
||||
}, u => u.Id == obj.Id);
|
||||
|
||||
}, u => u.Id == request.Id);
|
||||
SugarClient.Ado.CommitTran();
|
||||
|
||||
}
|
||||
|
||||
public WmsInboundOrderTblApp(ISqlSugarClient client, IAuth auth,
|
||||
WmsInboundOrderDtblApp wmsInboundOrderDtblApp) : base(client, auth)
|
||||
{
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <autogenerated>
|
||||
// This code was generated by a CodeSmith Template.
|
||||
//
|
||||
// DO NOT MODIFY contents of this file. Changes to this
|
||||
// file will be lost if the code is regenerated.
|
||||
// Author:Yubao Li
|
||||
// </autogenerated>
|
||||
//------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Infrastructure.Const;
|
||||
using OpenAuth.Repository.Core;
|
||||
|
||||
namespace OpenAuth.Repository.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程实例流转记录
|
||||
/// <para>记录某个流程实例所有已审批的从一个活动节点到下一个活动节点的操作人、操作时间</para>
|
||||
/// </summary>
|
||||
[Table("FlowInstanceTransitionHistory")]
|
||||
public partial class FlowInstanceTransitionHistory : StringEntity
|
||||
{
|
||||
public FlowInstanceTransitionHistory()
|
||||
{
|
||||
this.InstanceId = string.Empty;
|
||||
this.FromNodeId = string.Empty;
|
||||
this.FromNodeName = string.Empty;
|
||||
this.ToNodeId = string.Empty;
|
||||
this.ToNodeName = string.Empty;
|
||||
this.TransitionSate = 0;
|
||||
this.IsFinish = FlowInstanceStatus.Running;
|
||||
this.CreateDate = DateTime.Now;
|
||||
this.CreateUserId = string.Empty;
|
||||
this.CreateUserName = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实例Id
|
||||
/// </summary>
|
||||
[Description("实例Id")]
|
||||
public string InstanceId { get; set; }
|
||||
/// <summary>
|
||||
/// 开始节点Id
|
||||
/// </summary>
|
||||
[Description("开始节点Id")]
|
||||
public string FromNodeId { get; set; }
|
||||
/// <summary>
|
||||
/// 开始节点类型
|
||||
/// </summary>
|
||||
[Description("开始节点类型")]
|
||||
public string FromNodeType { get; set; }
|
||||
/// <summary>
|
||||
/// 开始节点名称
|
||||
/// </summary>
|
||||
[Description("开始节点名称")]
|
||||
public string FromNodeName { get; set; }
|
||||
/// <summary>
|
||||
/// 结束节点Id
|
||||
/// </summary>
|
||||
[Description("结束节点Id")]
|
||||
public string ToNodeId { get; set; }
|
||||
/// <summary>
|
||||
/// 结束节点类型
|
||||
/// </summary>
|
||||
[Description("结束节点类型")]
|
||||
public string ToNodeType { get; set; }
|
||||
/// <summary>
|
||||
/// 结束节点名称
|
||||
/// </summary>
|
||||
[Description("结束节点名称")]
|
||||
public string ToNodeName { get; set; }
|
||||
/// <summary>
|
||||
/// 转化状态
|
||||
/// </summary>
|
||||
[Description("转化状态")]
|
||||
public int TransitionSate { get; set; }
|
||||
/// <summary>
|
||||
/// 是否结束
|
||||
/// </summary>
|
||||
[Description("是否结束")]
|
||||
public int IsFinish { get; set; }
|
||||
/// <summary>
|
||||
/// 转化时间
|
||||
/// </summary>
|
||||
[Description("转化时间")]
|
||||
public System.DateTime CreateDate { get; set; }
|
||||
/// <summary>
|
||||
/// 操作人Id
|
||||
/// </summary>
|
||||
[Description("操作人Id")]
|
||||
public string CreateUserId { get; set; }
|
||||
/// <summary>
|
||||
/// 操作人名称
|
||||
/// </summary>
|
||||
[Description("操作人名称")]
|
||||
public string CreateUserName { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,6 @@ namespace OpenAuth.Repository
|
||||
public virtual DbSet<CategoryType> CategoryTypes { get; set; }
|
||||
public virtual DbSet<FlowInstance> FlowInstances { get; set; }
|
||||
public virtual DbSet<FlowInstanceOperationHistory> FlowInstanceOperationHistorys { get; set; }
|
||||
public virtual DbSet<FlowInstanceTransitionHistory> FlowInstanceTransitionHistorys { get; set; }
|
||||
public virtual DbSet<FlowScheme> FlowSchemes { get; set; }
|
||||
public virtual DbSet<Form> Forms { get; set; }
|
||||
public virtual DbSet<Module> Modules { get; set; }
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace OpenAuth.WebApi.Controllers
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public Response Update(AddOrUpdateOpenJobReq obj)
|
||||
public Response Update([FromBody]AddOrUpdateOpenJobReq obj)
|
||||
{
|
||||
var result = new Response();
|
||||
try
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
using Infrastructure;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenAuth.App;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.QueryObj;
|
||||
|
||||
namespace OpenAuth.WebApi.Controllers
|
||||
{
|
||||
@@ -12,13 +17,15 @@ namespace OpenAuth.WebApi.Controllers
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
[ApiExplorerSettings(GroupName = "系统配置_SysConf")]
|
||||
public class SysConfController :ControllerBase
|
||||
public class SysConfController : ControllerBase
|
||||
{
|
||||
private IOptions<AppSetting> _appConfiguration;
|
||||
private DbExtension _dbExtension;
|
||||
|
||||
public SysConfController(IOptions<AppSetting> appConfiguration)
|
||||
public SysConfController(IOptions<AppSetting> appConfiguration, DbExtension dbExtension)
|
||||
{
|
||||
_appConfiguration = appConfiguration;
|
||||
_dbExtension = dbExtension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -34,5 +41,53 @@ namespace OpenAuth.WebApi.Controllers
|
||||
Result = _appConfiguration.Value.IsIdentityAuth
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库表结构
|
||||
/// </summary>
|
||||
/// <param name="tableName">表名</param>
|
||||
/// <param name="externalDataSourceId">外部数据源ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public Response<List<SysTableColumn>> GetDbTableStructure(string tableName, string externalDataSourceId)
|
||||
{
|
||||
var result = new Response<List<SysTableColumn>>();
|
||||
try
|
||||
{
|
||||
result.Result = _dbExtension.GetDbTableStructure(tableName, externalDataSourceId).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Code = 500;
|
||||
result.Message = ex.Message;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库表结构(用于下拉框)
|
||||
/// </summary>
|
||||
/// <param name="tableName">表名</param>
|
||||
/// <param name="externalDataSourceId">外部数据源ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public TableData GetDbTableStructureForSelect(string tableName, string externalDataSourceId)
|
||||
{
|
||||
var result = new TableData();
|
||||
try
|
||||
{
|
||||
result.data = _dbExtension.GetDbTableStructure(tableName, externalDataSourceId).Select(u => new
|
||||
{
|
||||
Id = u.ColumnName,
|
||||
Name = u.ColumnName
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.code = 500;
|
||||
result.msg = ex.Message;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// Author:Yubao Li
|
||||
//------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using OpenAuth.Repository.Core;
|
||||
|
||||
104
OpenAuth.WebApi/Template/MultiTable/BuildApp.html
Normal file
104
OpenAuth.WebApi/Template/MultiTable/BuildApp.html
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using SqlSugar;
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class {ModuleCode} : SqlSugarBaseApp<{ClassName}>
|
||||
{
|
||||
private {SubModuleCode} _{SubClassName}App;
|
||||
/// <summary>
|
||||
/// 加载列表
|
||||
/// </summary>
|
||||
public async Task<TableData> Load(Query{ClassName}ListReq request)
|
||||
{
|
||||
var loginContext = _auth.GetCurrentUser();
|
||||
if (loginContext == null)
|
||||
{
|
||||
throw new CommonException("登录已过期", Define.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
var result = new TableData();
|
||||
var objs = GetDataPrivilege("u");
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Id.Contains(request.key));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
result.data = objs.OrderBy(u => u.Id)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).ToList();
|
||||
result.count = await objs.CountAsync();
|
||||
return result;
|
||||
}
|
||||
public void Add(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var obj = request.MapTo<{ClassName}>();
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
//todo:补充或调整自己需要的字段
|
||||
{InsertColumns}
|
||||
if(obj.KeyIsNull()) //如果主键为空,则生成默认值
|
||||
{
|
||||
obj.GenerateDefaultKeyVal();
|
||||
}
|
||||
|
||||
SugarClient.Ado.BeginTran();
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
if (request.{SubClassName}Reqs != null && request.{SubClassName}Reqs.Any())
|
||||
{
|
||||
foreach (var detail in request.{SubClassName}Reqs)
|
||||
{
|
||||
detail.{SubForeignKey} = obj.Id;
|
||||
_{SubClassName}App.Add(detail);
|
||||
}
|
||||
}
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
public void Update(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
SugarClient.Ado.BeginTran();
|
||||
if (request.{SubClassName}Reqs != null && request.{SubClassName}Reqs.Any())
|
||||
{
|
||||
//请求的id不在数据库的记录,需要删除
|
||||
var containids = request.{SubClassName}Reqs.Select(u => u.Id)
|
||||
.Where(u => !string.IsNullOrEmpty(u)).ToList();
|
||||
if (containids.Any())
|
||||
{
|
||||
SugarClient.Deleteable<{SubClassName}>(u => (!containids.Contains(u.Id)) && u.{SubForeignKey} == request.Id).ExecuteCommand();
|
||||
}
|
||||
//id为空的添加
|
||||
foreach (var detail in request.{SubClassName}Reqs.Where(u => string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
detail.{SubForeignKey} = request.Id;
|
||||
_{SubClassName}App.Add(detail);
|
||||
}
|
||||
//更新id相同的
|
||||
foreach (var detail in request.{SubClassName}Reqs.Where(u => !string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
_{SubClassName}App.Update(detail);
|
||||
}
|
||||
}
|
||||
Repository.Update(u => new {ClassName}
|
||||
{
|
||||
{UpdateColumns}
|
||||
//todo:补充或调整自己需要的字段
|
||||
}, u => u.Id == request.Id);
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
public {ModuleCode}(ISqlSugarClient client, IAuth auth,
|
||||
{SubModuleCode} {SubClassName}App) : base(client, auth)
|
||||
{
|
||||
_{SubClassName}App = {SubClassName}App;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using SqlSugar;
|
||||
namespace OpenAuth.App
|
||||
{
|
||||
public class {ModuleCode} : SqlSugarBaseApp<{ClassName}>
|
||||
{
|
||||
private {SubModuleCode} _{SubClassName}App;
|
||||
/// <summary>
|
||||
/// 加载列表
|
||||
/// </summary>
|
||||
public async Task<TableData> Load(Query{ClassName}ListReq request)
|
||||
{
|
||||
var loginContext = _auth.GetCurrentUser();
|
||||
if (loginContext == null)
|
||||
{
|
||||
throw new CommonException("登录已过期", Define.INVALID_TOKEN);
|
||||
}
|
||||
var columns = loginContext.GetTableColumns("{ClassName}");
|
||||
if (columns == null || columns.Count == 0)
|
||||
{
|
||||
throw new Exception("请在代码生成界面配置{ClassName}表的字段属性");
|
||||
}
|
||||
var result = new TableData();
|
||||
result.columnFields = columns;
|
||||
var objs = GetDataPrivilege("u");
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Id.Contains(request.key));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
var propertyStr = string.Join(',', columns.Select(u => u.ColumnName));
|
||||
result.data = objs.OrderBy(u => u.Id)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).Select($"{propertyStr}").ToList();
|
||||
result.count = await objs.CountAsync();
|
||||
return result;
|
||||
}
|
||||
public void Add(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var obj = request.MapTo<{ClassName}>();
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
//todo:补充或调整自己需要的字段
|
||||
{InsertColumns}
|
||||
if(obj.KeyIsNull()) //如果主键为空,则生成默认值
|
||||
{
|
||||
obj.GenerateDefaultKeyVal();
|
||||
}
|
||||
|
||||
SugarClient.Ado.BeginTran();
|
||||
SugarClient.Insertable(obj).ExecuteCommand();
|
||||
if (request.{SubClassName}Reqs != null && request.{SubClassName}Reqs.Any())
|
||||
{
|
||||
foreach (var detail in request.{SubClassName}Reqs)
|
||||
{
|
||||
detail.{SubForeignKey} = obj.Id;
|
||||
_{SubClassName}App.Add(detail);
|
||||
}
|
||||
}
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
public void Update(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
SugarClient.Ado.BeginTran();
|
||||
if (request.{SubClassName}Reqs != null && request.{SubClassName}Reqs.Any())
|
||||
{
|
||||
//请求的id不在数据库的记录,需要删除
|
||||
var containids = request.{SubClassName}Reqs.Select(u => u.Id)
|
||||
.Where(u => !string.IsNullOrEmpty(u)).ToList();
|
||||
if (containids.Any())
|
||||
{
|
||||
SugarClient.Deleteable<{SubClassName}>(u => (!containids.Contains(u.Id)) && u.{SubForeignKey} == request.Id).ExecuteCommand();
|
||||
}
|
||||
//id为空的添加
|
||||
foreach (var detail in request.{SubClassName}Reqs.Where(u => string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
detail.{SubForeignKey} = request.Id;
|
||||
_{SubClassName}App.Add(detail);
|
||||
}
|
||||
//更新id相同的
|
||||
foreach (var detail in request.{SubClassName}Reqs.Where(u => !string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
_{SubClassName}App.Update(detail);
|
||||
}
|
||||
}
|
||||
Repository.Update(u => new {ClassName}
|
||||
{
|
||||
{UpdateColumns}
|
||||
//todo:补充或调整自己需要的字段
|
||||
}, u => u.Id == request.Id);
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
public {ModuleCode}(ISqlSugarClient client, IAuth auth,
|
||||
{SubModuleCode} {SubClassName}App) : base(client, auth)
|
||||
{
|
||||
_{SubClassName}App = {SubClassName}App;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,395 +1,489 @@
|
||||
<!--
|
||||
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
|
||||
* @Description: 代码生成界面,采用的是固定头部结构
|
||||
* @
|
||||
* @Copyright (c) 2024 by yubaolee | ahfu~ , All Rights Reserved.
|
||||
* @Description: 代码生成器,多表模版,动态头部
|
||||
* @Copyright (c) 2025 by yubaolee | ahfu~ , All Rights Reserved.
|
||||
-->
|
||||
<template>
|
||||
<div class="flex-column flex">
|
||||
<div class="flex flex-column">
|
||||
<sticky>
|
||||
<div class="search-box">
|
||||
<el-input @keyup.enter="handleFilter" size="small" class="w-200 custom-input" :placeholder="'名称'"
|
||||
v-model="firstQuery.key"></el-input>
|
||||
<el-button class="filter-item custom-button" icon="el-icon-search" size="small" @click="handleFilter">
|
||||
搜索
|
||||
</el-button>
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{FirstTableName}s_query_conditions" :columns="firstHeaderList"
|
||||
:query-options="firstQuery" @search="handleFilter" class="flex-1" />
|
||||
<column-setting storageName="{FirstTableName}s_column_settings" :columns="firstHeaderList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
</sticky>
|
||||
<div class="flex-item flex flex-column main-context">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column">
|
||||
<auth-table size="small" ref="mainTable" :select-type="'radio'" :table-fields="firstHeaderList" :data="mainList"
|
||||
:v-loading="listLoading" @row-click="rowClickFirstTable"></auth-table>
|
||||
</el-card>
|
||||
<div class="flex flex-direction-r b-w p-r-10 p-t-10 p-b-10">
|
||||
<el-pagination v-show="firstTotal > 0" :total="firstTotal" v-model:currentPage="firstQuery.page"
|
||||
v-model:page-size="firstQuery.limit" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
<div class="flex-item flex">
|
||||
<div v-if="showTitleDialog" class="flex flex-column" style="width: 500px;">
|
||||
<el-card shadow="never" class="demo-card custom-card fh">
|
||||
<template #header>
|
||||
<div>
|
||||
<span v-if="radio == ''" class="f-12 f-b">表信息</span>
|
||||
<span v-else class="f-12 f-b">{{ tableName }}表信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<auth-form ref="dataForm" :rules="mainRules" :edit-model="editModel" :form-fields="firstHeaderList"
|
||||
v-model="firstTemp" :data="firstTemp" :col-num="2"></auth-form>
|
||||
</el-card>
|
||||
<!-- 主表 -->
|
||||
<div class="flex-item flex flex-column b-w" v-show="showNotFullScreen && !showDetailInTable">
|
||||
<auth-table size="small" ref="mainTable" id="mainTable" :table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading" @row-click="rowClickFirstTable"
|
||||
@row-dblclick="rowDblClickFirstTable"></auth-table>
|
||||
<div class="flex flex-direction-r p-r-10 b-w p-b-5 p-t-5">
|
||||
<el-pagination size="small" background v-show="firstTotal > 0" v-model:currentPage="firstQuery.page"
|
||||
v-model:page-size="firstQuery.limit" :page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="firstTotal" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
<div class="flex-item flex flex-column">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column" id="secondCard">
|
||||
</div>
|
||||
|
||||
<!-- 主表表单(双击显示在主表位置) -->
|
||||
<div class="flex-item flex flex-column b-w" v-show="showNotFullScreen && showDetailInTable">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column">
|
||||
<template #header>
|
||||
<div class="flex flex-center">
|
||||
|
||||
<el-button size="small" class="custom-button" :icon="ArrowLeft" @click="returnToList">
|
||||
返回列表
|
||||
</el-button>
|
||||
<el-button size="small" v-if="editModel" :icon="Close" class="custom-button" @click="cancelEdit">取消</el-button>
|
||||
<el-button size="small" class="custom-button" :icon="Check" v-if="editModel && dialogStatus == 'create'" type="primary"
|
||||
@click="createData">
|
||||
确认
|
||||
</el-button>
|
||||
<el-button size="small" class="custom-button" :icon="Check" v-else-if="editModel" type="primary"
|
||||
@click="updateData">确认</el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<auth-form ref="dataForm" :rules="mainRules" :edit-model="editModel" :form-fields="firstHeaderList"
|
||||
v-model="firstTemp" :data="firstTemp" :col-num="4"></auth-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<div class="flex-item flex">
|
||||
<div class="flex flex-column flex-item b-w" id="secondCard">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column">
|
||||
<template #header>
|
||||
<div class="flex flex-center">
|
||||
<el-icon v-if="showTitleDialog" @click="showTitleDialog = !showTitleDialog" :size="14">
|
||||
<ArrowLeftBold />
|
||||
</el-icon>
|
||||
<el-icon v-else @click="showTitleDialog = !showTitleDialog" :size="14">
|
||||
<ArrowRightBold />
|
||||
</el-icon>
|
||||
<span v-if="radio == ''" class="flex-item f-12 f-b">
|
||||
表字段信息(修改后,编辑框内回车生效)
|
||||
明细列表(修改后,编辑框内回车生效)
|
||||
</span>
|
||||
<span v-else class="flex-item f-12 f-b">
|
||||
{{ tableName }}表字段信息(修改后,编辑框内回车生效)
|
||||
{{ tableName }}明细列表(修改后,编辑框内回车生效)
|
||||
</span>
|
||||
<el-button size="small" v-if="editModel" :icon="Plus" @click="onBtnClicked('btnAddDetail')">
|
||||
添加
|
||||
</el-button>
|
||||
<el-button size="small" v-if="editModel" :icon="Delete" @click="onBtnClicked('btnDelDetail')">
|
||||
删除
|
||||
</el-button>
|
||||
<column-setting buttonClass="filter-item" storageName="{SecondTableName}s_column_settings"
|
||||
:columns="secondHeaderList" @update:columns="updateSecondColumns" class="ml-2" />
|
||||
<el-button size="small" :icon="showNotFullScreen ? FullScreen : Close"
|
||||
@click="showNotFullScreen = !showNotFullScreen">
|
||||
{{ showNotFullScreen ? '全屏' : '退出全屏' }}
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<auth-table size="small" ref="secondTable" :editModel="editModel" :table-fields="secondHeaderList"
|
||||
:data="secondList" @row-click="rowClickSecondTable" @selection-change="selChangeSecondTable"
|
||||
@item-change="handleUpdateDetail"></auth-table>
|
||||
:data="secondList" @row-click="rowClickSecondTable" @selection-change="selChangeSecondTable"
|
||||
@item-change="handleUpdateDetail"></auth-table>
|
||||
</el-card>
|
||||
<div class="flex flex-direction-r p-r-10 b-w p-b-5 p-t-5">
|
||||
<el-pagination v-show="secondTotal > 0" :total="secondTotal" v-model:page="secondQuery.page"
|
||||
v-model:limit="secondQuery.limit" @current-change="handleSecondPage" />
|
||||
<el-pagination size="small" background v-show="secondTotal > 0" :currentPage="secondQuery.page"
|
||||
:page-size="secondQuery.limit" :total="secondTotal" @current-change="handleSecondPage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="editModel" class="p-t-5 p-b-5 t-r b-w p-r-10 border-t-2">
|
||||
<el-button @click="editModel = false">取消</el-button>
|
||||
<el-button v-if="dialogStatus == 'create'" type="primary" @click="createData">
|
||||
确认
|
||||
</el-button>
|
||||
<el-button v-else type="primary" @click="updateData">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<print-preview ref="preView" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
//引入核心框架
|
||||
import { ElMessage, ElNotification } from 'element-plus'
|
||||
import { Refresh, Delete } from '@element-plus/icons-vue'
|
||||
import { onMounted, ref, reactive, nextTick, computed } from 'vue'
|
||||
import { mapGetters, useStore } from 'vuex'
|
||||
//引入API,共用方法
|
||||
import * as {FirstTableName}s from '@/api/{FirstTableName}s'
|
||||
import * as {SecondTableName}s from '@/api/{SecondTableName}s'
|
||||
import ColumnDefine from '@/interface/columnDefine'
|
||||
import { typeLists } from '@/utils/const.js'
|
||||
import { parseTime, defaultVal } from '@/utils/index'
|
||||
import { delrows } from '@/utils/delRows.js'
|
||||
//引入组件
|
||||
import Sticky from '@/components/Sticky/index.vue'
|
||||
import permissionBtn from '@/components/PermissionBtn/index.vue'
|
||||
import AuthForm from '../../components/Base/AuthForm.vue'
|
||||
import AuthTable from '../../components/Base/AuthTable.vue'
|
||||
const firstHeaderList = ref([]) //列表头
|
||||
const secondHeaderList = ref([]) //明细列表头
|
||||
const multipleSelection = ref([]) //明细中checkbox选中的值
|
||||
const mainList = ref([]) //主列表值
|
||||
const secondList = ref([]) //明细列表值
|
||||
const firstTotal = ref(0) //主列表总条数
|
||||
const secondTotal = ref(0) //主列表总条数
|
||||
const listLoading = ref(true) //进度条
|
||||
const editModel = ref(false) //是否为编辑状态
|
||||
const editType = ref('edit')
|
||||
const dialogStatus = ref('') //主修改对话框状态create/update
|
||||
const radio = ref('') //主列表选项值
|
||||
const tableName = ref('')
|
||||
let firstTemp = reactive({}) //当前选中的头信息
|
||||
let selectRow = reactive({})
|
||||
const firstQuery = reactive({
|
||||
// 查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
key: undefined,
|
||||
})
|
||||
const secondQuery = reactive({
|
||||
// 明细查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
customerKey: undefined,
|
||||
})
|
||||
const showTitleDialog = ref(true) //是否显示左下主列表详情值
|
||||
const mainRules = reactive({
|
||||
Id: [
|
||||
{
|
||||
required: true,
|
||||
message: 'id不能为空'
|
||||
},
|
||||
],
|
||||
})
|
||||
//组件refs
|
||||
const mainTable = ref(null)
|
||||
const dataForm = ref(null)
|
||||
const secondTable = ref(null)
|
||||
const store = useStore()
|
||||
const defaultorgid = computed(() => store.getters.defaultorgid)
|
||||
onMounted(() => {
|
||||
initCfg()
|
||||
getList()
|
||||
})
|
||||
const initCfg = function () {
|
||||
//引入核心框架
|
||||
import { ElMessage, ElNotification } from 'element-plus'
|
||||
import { Plus, Delete, FullScreen, Close, Check, ArrowLeft } from '@element-plus/icons-vue'
|
||||
import { onMounted, ref, reactive, nextTick, computed, defineComponent, watch } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
//引入API,共用方法
|
||||
import * as {FirstTableName}s from '@/api/{FirstTableName}s'
|
||||
import * as {SecondTableName}s from '@/api/{SecondTableName}s'
|
||||
import { parseTime, defaultVal } from '@/utils/index'
|
||||
import common from "@/extensions/common.js"
|
||||
import { getItem } from '@/utils/storage'
|
||||
import ColumnDefine from '@/interface/columnDefine'
|
||||
|
||||
//引入组件
|
||||
import Sticky from '@/components/Sticky/index.vue'
|
||||
import permissionBtn from '@/components/PermissionBtn/index.vue'
|
||||
import AuthForm from '../../components/Base/AuthForm.vue'
|
||||
import AuthTable from '../../components/Base/AuthTable.vue'
|
||||
import QueryBuilder from '@/components/QueryBuilder/index.vue'
|
||||
import ColumnSetting from '@/components/ColumnSetting/index.vue'
|
||||
|
||||
const firstHeaderList = ref([]) //列表头
|
||||
const secondHeaderList = ref([]) //明细列表头
|
||||
const multipleSelection = ref([]) //明细中checkbox选中的值
|
||||
const mainList = ref([]) //主列表值
|
||||
const secondList = ref([]) //明细列表值
|
||||
const firstTotal = ref(0) //主列表总条数
|
||||
const secondTotal = ref(0) //主列表总条数
|
||||
const listLoading = ref(true) //进度条
|
||||
const editModel = ref(false) //是否为编辑状态
|
||||
const editType = ref('edit')
|
||||
const dialogStatus = ref('') //主修改对话框状态create/update
|
||||
const radio = ref('') //主列表选项值
|
||||
const tableName = ref('')
|
||||
const showDetailInTable = ref(false) // 是否在主表位置显示详情
|
||||
let firstTemp = reactive({}) //当前选中的主表项
|
||||
let selectRow = reactive({}) //当前选中的主表项
|
||||
//组件refs
|
||||
const mainTable = ref(null)
|
||||
const dataForm = ref(null)
|
||||
const secondTable = ref(null)
|
||||
const preView = ref(null)
|
||||
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const defaultorgid = computed(() => store.getters.defaultorgid)
|
||||
const { showPreview, printPreview, createFlowInstance } = common()
|
||||
|
||||
const firstQuery = reactive({
|
||||
// 查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
key: undefined,
|
||||
})
|
||||
const secondQuery = reactive({
|
||||
// 明细查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
customerKey: undefined,
|
||||
})
|
||||
|
||||
const showNotFullScreen = ref(true) //明细列表是否全屏
|
||||
watch(showNotFullScreen, () => {
|
||||
secondQuery.limit = showNotFullScreen.value ? 10 : 20
|
||||
querySecondList(radio.value)
|
||||
})
|
||||
|
||||
const mainRules = reactive({
|
||||
Id: [
|
||||
{
|
||||
required: true,
|
||||
message: '入库通知单号不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
initCfg()
|
||||
getList()
|
||||
})
|
||||
|
||||
const initCfg = function () {
|
||||
firstHeaderList.value = [
|
||||
{FirstHeaderList}
|
||||
]
|
||||
secondHeaderList.value = [
|
||||
{SecondHeaderList}
|
||||
]
|
||||
}
|
||||
// ------------------------通用处理函数-------------------------------------
|
||||
const onBtnClicked = function (domId, callback) {
|
||||
switch (domId) {
|
||||
case 'btnAdd': // 添加新记录
|
||||
resetFirstTemp()
|
||||
secondList.value = []
|
||||
dialogStatus.value = 'create'
|
||||
editModel.value = true
|
||||
tableName.value = '新建'
|
||||
editType.value = 'add'
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnEdit': // 编辑头
|
||||
Object.assign(firstTemp, selectRow)
|
||||
if (firstTemp.id === '') {
|
||||
editModel.value = false
|
||||
ElMessage({
|
||||
message: '请选择要修改的项',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
dialogStatus.value = 'update'
|
||||
editModel.value = true
|
||||
editType.value = 'edit'
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnDel': // 删除主表
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage({
|
||||
message: '请选择要删除的项',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
handleFirstDel(firstTemp)
|
||||
break
|
||||
case 'btnAddDetail': // 添加明细行
|
||||
handleAddOrderDetail()
|
||||
break
|
||||
case 'btnDelDetail': // 删除明细行
|
||||
if (multipleSelection.value.length < 1) {
|
||||
ElMessage({
|
||||
message: '至少删除一个',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
handleSecondDel(multipleSelection.value)
|
||||
break
|
||||
case 'btnExport':
|
||||
mainTable.value.exportExcel(`表结构${parseTime(new Date())}`, callback)
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
||||
// 尝试从存储中加载列设置
|
||||
const savedSettings = getItem('{FirstTableName}s_column_settings')
|
||||
if (savedSettings) {
|
||||
// 将保存的设置合并到当前列表
|
||||
firstHeaderList.value = savedSettings
|
||||
}
|
||||
|
||||
const secondSavedSettings = getItem('{SecondTableName}s_column_settings')
|
||||
if (secondSavedSettings) {
|
||||
// 将保存的设置合并到当前列表
|
||||
secondHeaderList.value = secondSavedSettings
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------主数据列表处理------------------------------------
|
||||
const getList = function () {
|
||||
listLoading.value = true
|
||||
{FirstTableName}s.getList(firstQuery).then(response => {
|
||||
mainList.value = response.data
|
||||
firstTotal.value = response.count
|
||||
if (firstTotal.value > 0) {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
// ------------------------通用处理函数-------------------------------------
|
||||
const onBtnClicked = (domId, args, callback) => {
|
||||
switch (domId) {
|
||||
case 'btnAdd': // 添加
|
||||
resetFirstTemp()
|
||||
secondList.value = []
|
||||
dialogStatus.value = 'create'
|
||||
editModel.value = true
|
||||
tableName.value = '新建'
|
||||
editType.value = 'add'
|
||||
showDetailInTable.value = true // 显示详情编辑界面
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnEdit': // 编辑
|
||||
Object.assign(firstTemp, selectRow)
|
||||
if (firstTemp.id === '') {
|
||||
editModel.value = false
|
||||
ElMessage.error('请选择要修改的项')
|
||||
return
|
||||
}
|
||||
listLoading.value = false
|
||||
})
|
||||
}
|
||||
const rowClickFirstTable = function (row) {
|
||||
// 点击行
|
||||
radio.value = row.id
|
||||
tableName.value = row.tableName
|
||||
secondQuery.page = 1
|
||||
secondQuery.limit = 10
|
||||
querySecondList(radio.value)
|
||||
showTitleDetail(row)
|
||||
}
|
||||
const handleFilter = function () {
|
||||
firstQuery.page = 1
|
||||
getList()
|
||||
}
|
||||
const handleSizeChange = function (val) {
|
||||
firstQuery.limit = val
|
||||
getList()
|
||||
}
|
||||
const handleCurrentChange = function (val) {
|
||||
firstQuery.page = val
|
||||
getList()
|
||||
}
|
||||
const resetFirstTemp = function () {
|
||||
firstHeaderList.value.forEach(item => {
|
||||
firstTemp[item.columnName] = defaultVal(item.entityType)
|
||||
})
|
||||
firstTemp.namespace = 'OpenAuth.Repository.Domain'
|
||||
dialogStatus.value = 'update'
|
||||
editModel.value = true
|
||||
editType.value = 'edit'
|
||||
showDetailInTable.value = true // 编辑时显示详情界面
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnDel': // 删除
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage.error('请选择要删除的项')
|
||||
return
|
||||
}
|
||||
handleFirstDel(firstTemp)
|
||||
break
|
||||
case 'btnPrint':
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage.error('请选择要打印的内容')
|
||||
return
|
||||
}
|
||||
showPreview(preView.value, args, firstTemp);
|
||||
break
|
||||
case 'btnFlowscheme':
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage.error('请选择要送审的内容')
|
||||
return
|
||||
}
|
||||
createFlowInstance(args, firstTemp);
|
||||
break
|
||||
case 'btnAddDetail': // 添加明细行
|
||||
handleAddOrderDetail()
|
||||
break
|
||||
case 'btnDelDetail': // 删除明细行
|
||||
if (multipleSelection.value.length < 1) {
|
||||
ElMessage.error('至少删除一个')
|
||||
return
|
||||
}
|
||||
handleSecondDel(multipleSelection.value)
|
||||
break
|
||||
case 'btnExport': //导出
|
||||
mainTable.value.exportExcel(`表结构${parseTime(new Date())}`, callback)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const setDetails = tempData => {
|
||||
// 处理明细
|
||||
tempData.{SecondTableName}Reqs = []
|
||||
secondList.value.length > 0 &&
|
||||
// ------------------------主数据列表处理------------------------------------
|
||||
|
||||
const getList = () => {
|
||||
listLoading.value = true
|
||||
{FirstTableName}s.getList(firstQuery).then(response => {
|
||||
mainList.value = response.data
|
||||
firstTotal.value = response.count
|
||||
if (firstTotal.value > 0) {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
}
|
||||
listLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 处理列设置更新
|
||||
const handleColumnsUpdate = (columns) => {
|
||||
if (columns) {
|
||||
// 更新列显示配置
|
||||
firstHeaderList.value = columns
|
||||
} else {
|
||||
// 如果是null,则重置回默认配置
|
||||
initCfg()
|
||||
}
|
||||
}
|
||||
|
||||
const updateSecondColumns = (columns) => {
|
||||
if (columns) {
|
||||
// 更新列显示配置
|
||||
secondHeaderList.value = columns
|
||||
} else {
|
||||
// 如果是null,则重置回默认配置
|
||||
initCfg()
|
||||
}
|
||||
}
|
||||
|
||||
const rowClickFirstTable = row => {
|
||||
// 点击行
|
||||
mainTable.value.clearSelection()
|
||||
mainTable.value.toggleRowSelection(row)
|
||||
|
||||
radio.value = row.id
|
||||
tableName.value = row.id
|
||||
secondQuery.page = 1
|
||||
secondQuery.limit = 10
|
||||
querySecondList(radio.value)
|
||||
showTitleDetail(row)
|
||||
}
|
||||
|
||||
const handleFilter = () => {
|
||||
firstQuery.page = 1
|
||||
getList()
|
||||
}
|
||||
const handleSizeChange = val => {
|
||||
firstQuery.limit = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = val => {
|
||||
firstQuery.page = val
|
||||
getList()
|
||||
}
|
||||
const resetFirstTemp = () => {
|
||||
firstHeaderList.value.forEach(item => {
|
||||
firstTemp[item.columnName] = defaultVal(item.entityType)
|
||||
})
|
||||
}
|
||||
const createData = () => {
|
||||
// 保存提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
tempData.OrgId = defaultorgid.value
|
||||
{FirstTableName}s.add(tempData).then(resp => {
|
||||
if (resp.result != undefined) {
|
||||
//如果服务器返回生成的ID
|
||||
firstTemp.id = resp.result
|
||||
}
|
||||
mainList.value.unshift(firstTemp)
|
||||
editModel.value = false
|
||||
showDetailInTable.value = false // 保存后返回列表视图
|
||||
rowClickFirstTable(firstTemp)
|
||||
ElNotification.success('创建成功')
|
||||
})
|
||||
})
|
||||
}
|
||||
const showTitleDetail = row => {
|
||||
// 弹出编辑框
|
||||
Object.assign(selectRow, row) // 新增订单时保存当前选中行
|
||||
Object.assign(firstTemp, row) // copy obj
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
}
|
||||
const setDetails = tempData => {
|
||||
// 处理明细
|
||||
tempData.{SecondTableName}Reqs = []
|
||||
secondList.value.length > 0 &&
|
||||
secondList.value.forEach(item => {
|
||||
const obj = {}
|
||||
|
||||
secondHeaderList.value.forEach(header => {
|
||||
obj[header.columnName] =
|
||||
item[header.columnName] || defaultVal(header.entityType)
|
||||
item[header.columnName] || defaultVal(header.entityType)
|
||||
})
|
||||
|
||||
tempData.{SecondTableName}Reqs.push(obj)
|
||||
})
|
||||
return tempData
|
||||
}
|
||||
|
||||
const createData = function () {
|
||||
// 保存提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
tempData.OrgId = defaultorgid.value
|
||||
{FirstTableName}s.add(tempData).then(resp => {
|
||||
tempData.id = resp.result
|
||||
mainList.value.unshift(tempData)
|
||||
editModel.value = false
|
||||
rowClickFirstTable(tempData)
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '创建成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const showTitleDetail = function (row) {
|
||||
// 弹出编辑框
|
||||
Object.assign(selectRow, row) // 新增订单时保存当前选中行
|
||||
Object.assign(firstTemp, row) // copy obj
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
}
|
||||
const updateData = function () {
|
||||
// 更新提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of mainList.value) {
|
||||
if (v.id === firstTemp.id) {
|
||||
const index = mainList.value.indexOf(v)
|
||||
mainList.value.splice(index, 1, tempData)
|
||||
break
|
||||
}
|
||||
return tempData
|
||||
}
|
||||
const updateData = () => {
|
||||
// 更新提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of mainList.value) {
|
||||
if (v.id === firstTemp.id) {
|
||||
const index = mainList.value.indexOf(v)
|
||||
mainList.value.splice(index, 1, tempData)
|
||||
break
|
||||
}
|
||||
editModel.value = false
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const handleFirstDel = function (row) {
|
||||
// 删除头
|
||||
{FirstTableName}s.del([row.id]).then(() => {
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
mainList.value = mainList.value.filter(item => item.id !== row.id)
|
||||
if (mainList.value.length > 0) {
|
||||
nextTick(() => {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
})
|
||||
return
|
||||
}
|
||||
secondList.value = []
|
||||
showTitleDetail({})
|
||||
editModel.value = false
|
||||
showDetailInTable.value = false // 保存后返回列表视图
|
||||
ElNotification.success('更新成功')
|
||||
})
|
||||
}
|
||||
// ------------------------明细列表处理-------------------------------------
|
||||
const handleSecondPage = function (e) {
|
||||
secondQuery.page = e
|
||||
querySecondList(radio.value)
|
||||
}
|
||||
const querySecondList = id => {
|
||||
{SecondTableName}s
|
||||
.getList({
|
||||
{ParentTableId}: id,
|
||||
page: secondQuery.page,
|
||||
limit: secondQuery.limit,
|
||||
key: secondQuery.customerKey,
|
||||
})
|
||||
.then(res => {
|
||||
}
|
||||
const handleFirstDel = row => {
|
||||
// 主表删除处理
|
||||
{FirstTableName}s.del([row.id]).then(() => {
|
||||
ElNotification.success('删除成功')
|
||||
mainList.value = mainList.value.filter(item => item.id !== row.id)
|
||||
if (mainList.value.length > 0) {
|
||||
nextTick(() => {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
})
|
||||
return
|
||||
}
|
||||
secondList.value = []
|
||||
showTitleDetail({})
|
||||
})
|
||||
}
|
||||
|
||||
//返回列表
|
||||
const returnToList = () => {
|
||||
showDetailInTable.value = false
|
||||
cancelEdit()
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = () => {
|
||||
editModel.value = false
|
||||
if (dialogStatus.value === 'create') {
|
||||
showDetailInTable.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 主列表双击行事件
|
||||
const rowDblClickFirstTable = row => {
|
||||
rowClickFirstTable(row) // 先执行单击行的逻辑
|
||||
showDetailInTable.value = true // 在主表位置显示详情
|
||||
}
|
||||
|
||||
// ------------------------明细列表处理-------------------------------------
|
||||
const handleSecondPage = e => {
|
||||
secondQuery.page = e
|
||||
querySecondList(radio.value)
|
||||
}
|
||||
|
||||
const querySecondList = id => {
|
||||
{SecondTableName}s
|
||||
.getList({
|
||||
{ParentTableId}: id,
|
||||
page: secondQuery.page,
|
||||
limit: secondQuery.limit,
|
||||
key: secondQuery.customerKey,
|
||||
})
|
||||
.then(res => {
|
||||
secondTotal.value = res.count
|
||||
secondList.value = res.data
|
||||
})
|
||||
}
|
||||
const rowClickSecondTable = function (row) {
|
||||
// 行点击事件
|
||||
secondTable.value.clearSelection()
|
||||
secondTable.value.toggleRowSelection(row)
|
||||
}
|
||||
const handleSecondDel = function (rows) {
|
||||
{SecondTableName}s.del(rows.map(item => item.id)).then(() => {
|
||||
rows.forEach(row => {
|
||||
secondList.value = secondList.value.filter(item => item.id !== row.id)
|
||||
})
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
}
|
||||
const rowClickSecondTable = row => {
|
||||
// 行点击事件
|
||||
secondTable.value.clearSelection()
|
||||
secondTable.value.toggleRowSelection(row)
|
||||
}
|
||||
const handleSecondDel = function (rows) {
|
||||
{SecondTableName}s.del(rows.map(item => item.id)).then(() => {
|
||||
rows.forEach(row => {
|
||||
secondList.value = secondList.value.filter(item => item.id !== row.id)
|
||||
})
|
||||
}
|
||||
const selChangeSecondTable = function (val) {
|
||||
// 明细选中事件
|
||||
multipleSelection.value = val
|
||||
}
|
||||
const handleUpdateDetail = function (item) {
|
||||
// 同步表数据结构
|
||||
{SecondTableName}s.update(item).then(() => {
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
}
|
||||
ElNotification.success('删除成功')
|
||||
})
|
||||
}
|
||||
const selChangeSecondTable = val => {
|
||||
// 明细选中事件
|
||||
multipleSelection.value = val
|
||||
}
|
||||
const handleAddOrderDetail = () => {
|
||||
// 添加明细
|
||||
const obj = {}
|
||||
secondHeaderList.value.forEach(header => {
|
||||
obj[header.columnName] = defaultVal(header.entityType)
|
||||
})
|
||||
obj.orderId = firstTemp.id
|
||||
|
||||
secondList.value.push(Object.assign({}, obj))
|
||||
}
|
||||
const handleUpdateDetail = item => {
|
||||
// 回车保存明细,如果不需要可以不用
|
||||
{SecondTableName}s.update(item).then(() => {
|
||||
ElNotification.success('成功')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -1,409 +1,491 @@
|
||||
<!--
|
||||
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
|
||||
* @Description: 代码生成界面,动态头部
|
||||
* @
|
||||
* @Copyright (c) 2024 by yubaolee | ahfu~ , All Rights Reserved.
|
||||
* @Description: 代码生成器,多表模版,动态头部
|
||||
* @Copyright (c) 2025 by yubaolee | ahfu~ , All Rights Reserved.
|
||||
-->
|
||||
<template>
|
||||
<div class="flex-column flex">
|
||||
<div class="flex flex-column">
|
||||
<sticky>
|
||||
<div class="search-box">
|
||||
<el-input @keyup.enter="handleFilter" size="small" class="w-200 custom-input" :placeholder="'名称'"
|
||||
v-model="firstQuery.key"></el-input>
|
||||
<el-button class="filter-item custom-button" icon="el-icon-search" size="small" @click="handleFilter">
|
||||
搜索
|
||||
</el-button>
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{FirstTableName}s_query_conditions" :columns="firstHeaderList"
|
||||
:query-options="firstQuery" @search="handleFilter" class="flex-1" />
|
||||
<column-setting storageName="{FirstTableName}s_column_settings" :columns="firstHeaderList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
</sticky>
|
||||
<div class="flex-item flex flex-column main-context">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column">
|
||||
<auth-table size="small" ref="mainTable" :select-type="'radio'" :table-fields="firstHeaderList" :data="mainList"
|
||||
:v-loading="listLoading" @row-click="rowClickFirstTable"></auth-table>
|
||||
</el-card>
|
||||
<div class="flex flex-direction-r b-w p-r-10 p-t-10 p-b-10">
|
||||
<el-pagination v-show="firstTotal > 0" :total="firstTotal" v-model:currentPage="firstQuery.page"
|
||||
v-model:page-size="firstQuery.limit" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
<div class="flex-item flex">
|
||||
<div v-if="showTitleDialog" class="flex flex-column" style="width: 500px;">
|
||||
<el-card shadow="never" class="demo-card custom-card fh">
|
||||
<template #header>
|
||||
<div>
|
||||
<span v-if="radio == ''" class="f-12 f-b">表信息</span>
|
||||
<span v-else class="f-12 f-b">{{ tableName }}表信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<auth-form ref="dataForm" :rules="mainRules" :edit-model="editModel" :form-fields="firstHeaderList"
|
||||
v-model="firstTemp" :data="firstTemp" :col-num="2"></auth-form>
|
||||
</el-card>
|
||||
<!-- 主表 -->
|
||||
<div class="flex-item flex flex-column b-w" v-show="showNotFullScreen && !showDetailInTable">
|
||||
<auth-table size="small" ref="mainTable" id="mainTable" :table-fields="firstHeaderList" :data="mainList" :v-loading="listLoading" @row-click="rowClickFirstTable"
|
||||
@row-dblclick="rowDblClickFirstTable"></auth-table>
|
||||
<div class="flex flex-direction-r p-r-10 b-w p-b-5 p-t-5">
|
||||
<el-pagination size="small" background v-show="firstTotal > 0" v-model:currentPage="firstQuery.page"
|
||||
v-model:page-size="firstQuery.limit" :page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="firstTotal" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
<div class="flex-item flex flex-column">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column" id="secondCard">
|
||||
</div>
|
||||
|
||||
<!-- 主表表单(双击显示在主表位置) -->
|
||||
<div class="flex-item flex flex-column b-w" v-show="showNotFullScreen && showDetailInTable">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column">
|
||||
<template #header>
|
||||
<div class="flex flex-center">
|
||||
|
||||
<el-button size="small" class="custom-button" :icon="ArrowLeft" @click="returnToList">
|
||||
返回列表
|
||||
</el-button>
|
||||
<el-button size="small" v-if="editModel" :icon="Close" class="custom-button" @click="cancelEdit">取消</el-button>
|
||||
<el-button size="small" class="custom-button" :icon="Check" v-if="editModel && dialogStatus == 'create'" type="primary"
|
||||
@click="createData">
|
||||
确认
|
||||
</el-button>
|
||||
<el-button size="small" class="custom-button" :icon="Check" v-else-if="editModel" type="primary"
|
||||
@click="updateData">确认</el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<auth-form ref="dataForm" :rules="mainRules" :edit-model="editModel" :form-fields="firstHeaderList"
|
||||
v-model="firstTemp" :data="firstTemp" :col-num="4"></auth-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<div class="flex-item flex">
|
||||
<div class="flex flex-column flex-item b-w" id="secondCard">
|
||||
<el-card shadow="never" class="custom-card flex-item flex flex-column">
|
||||
<template #header>
|
||||
<div class="flex flex-center">
|
||||
<el-icon v-if="showTitleDialog" @click="showTitleDialog = !showTitleDialog" :size="14">
|
||||
<ArrowLeftBold />
|
||||
</el-icon>
|
||||
<el-icon v-else @click="showTitleDialog = !showTitleDialog" :size="14">
|
||||
<ArrowRightBold />
|
||||
</el-icon>
|
||||
<span v-if="radio == ''" class="flex-item f-12 f-b">
|
||||
表字段信息(修改后,编辑框内回车生效)
|
||||
明细列表(修改后,编辑框内回车生效)
|
||||
</span>
|
||||
<span v-else class="flex-item f-12 f-b">
|
||||
{{ tableName }}表字段信息(修改后,编辑框内回车生效)
|
||||
{{ tableName }}明细列表(修改后,编辑框内回车生效)
|
||||
</span>
|
||||
<el-button size="small" v-if="editModel" :icon="Plus" @click="onBtnClicked('btnAddDetail')">
|
||||
添加
|
||||
</el-button>
|
||||
<el-button size="small" v-if="editModel" :icon="Delete" @click="onBtnClicked('btnDelDetail')">
|
||||
删除
|
||||
</el-button>
|
||||
<column-setting buttonClass="filter-item" storageName="{SecondTableName}s_column_settings"
|
||||
:columns="secondHeaderList" @update:columns="updateSecondColumns" class="ml-2" />
|
||||
<el-button size="small" :icon="showNotFullScreen ? FullScreen : Close"
|
||||
@click="showNotFullScreen = !showNotFullScreen">
|
||||
{{ showNotFullScreen ? '全屏' : '退出全屏' }}
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<auth-table size="small" ref="secondTable" :editModel="editModel" :table-fields="secondHeaderList"
|
||||
:data="secondList" @row-click="rowClickSecondTable" @selection-change="selChangeSecondTable"
|
||||
@item-change="handleUpdateDetail"></auth-table>
|
||||
:data="secondList" @row-click="rowClickSecondTable" @selection-change="selChangeSecondTable"
|
||||
@item-change="handleUpdateDetail"></auth-table>
|
||||
</el-card>
|
||||
<div class="flex flex-direction-r p-r-10 b-w p-b-5 p-t-5">
|
||||
<el-pagination v-show="secondTotal > 0" :total="secondTotal" v-model:page="secondQuery.page"
|
||||
v-model:limit="secondQuery.limit" @current-change="handleSecondPage" />
|
||||
<el-pagination size="small" background v-show="secondTotal > 0" :currentPage="secondQuery.page"
|
||||
:page-size="secondQuery.limit" :total="secondTotal" @current-change="handleSecondPage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="editModel" class="p-t-5 p-b-5 t-r b-w p-r-10 border-t-2">
|
||||
<el-button @click="editModel = false">取消</el-button>
|
||||
<el-button v-if="dialogStatus == 'create'" type="primary" @click="createData">
|
||||
确认
|
||||
</el-button>
|
||||
<el-button v-else type="primary" @click="updateData">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<print-preview ref="preView" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
//引入核心框架
|
||||
import { ElMessage, ElNotification } from 'element-plus'
|
||||
import { Refresh, Delete } from '@element-plus/icons-vue'
|
||||
import { onMounted, ref, reactive, nextTick, computed } from 'vue'
|
||||
import { mapGetters, useStore } from 'vuex'
|
||||
//引入API,共用方法
|
||||
import * as {FirstTableName}s from '@/api/{FirstTableName}s'
|
||||
import * as {SecondTableName}s from '@/api/{SecondTableName}s'
|
||||
import ColumnDefine from '@/interface/columnDefine'
|
||||
import { typeLists } from '@/utils/const.js'
|
||||
import { parseTime, defaultVal } from '@/utils/index'
|
||||
import { delrows } from '@/utils/delRows.js'
|
||||
//引入组件
|
||||
import Sticky from '@/components/Sticky/index.vue'
|
||||
import permissionBtn from '@/components/PermissionBtn/index.vue'
|
||||
import AuthForm from '../../components/Base/AuthForm.vue'
|
||||
import AuthTable from '../../components/Base/AuthTable.vue'
|
||||
const firstHeaderList = ref([]) //列表头
|
||||
const secondHeaderList = ref([]) //明细列表头
|
||||
const multipleSelection = ref([]) //明细中checkbox选中的值
|
||||
const mainList = ref([]) //主列表值
|
||||
const secondList = ref([]) //明细列表值
|
||||
const firstTotal = ref(0) //主列表总条数
|
||||
const secondTotal = ref(0) //主列表总条数
|
||||
const listLoading = ref(true) //进度条
|
||||
const editModel = ref(false) //是否为编辑状态
|
||||
const editType = ref('edit')
|
||||
const dialogStatus = ref('') //主修改对话框状态create/update
|
||||
const radio = ref('') //主列表选项值
|
||||
const tableName = ref('')
|
||||
let firstTemp = reactive({}) //当前选中的头信息
|
||||
let selectRow = reactive({})
|
||||
const firstQuery = reactive({
|
||||
// 查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
key: undefined,
|
||||
})
|
||||
const secondQuery = reactive({
|
||||
// 明细查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
customerKey: undefined,
|
||||
})
|
||||
const showTitleDialog = ref(true) //是否显示左下主列表详情值
|
||||
const mainRules = reactive({
|
||||
Id: [
|
||||
{
|
||||
required: true,
|
||||
message: 'id不能为空'
|
||||
},
|
||||
],
|
||||
})
|
||||
//组件refs
|
||||
const mainTable = ref(null)
|
||||
const dataForm = ref(null)
|
||||
const secondTable = ref(null)
|
||||
const store = useStore()
|
||||
const defaultorgid = computed(() => store.getters.defaultorgid)
|
||||
onMounted(() => {
|
||||
initCfg()
|
||||
getList()
|
||||
})
|
||||
const initCfg = function () {
|
||||
firstHeaderList.value = [
|
||||
{FirstHeaderList}
|
||||
]
|
||||
secondHeaderList.value = [
|
||||
{SecondHeaderList}
|
||||
]
|
||||
}
|
||||
// ------------------------通用处理函数-------------------------------------
|
||||
const onBtnClicked = function (domId, callback) {
|
||||
switch (domId) {
|
||||
case 'btnAdd': // 添加新记录
|
||||
resetFirstTemp()
|
||||
secondList.value = []
|
||||
dialogStatus.value = 'create'
|
||||
editModel.value = true
|
||||
tableName.value = '新建'
|
||||
editType.value = 'add'
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnEdit': // 编辑头
|
||||
Object.assign(firstTemp, selectRow)
|
||||
if (firstTemp.id === '') {
|
||||
editModel.value = false
|
||||
ElMessage({
|
||||
message: '请选择要修改的项',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
dialogStatus.value = 'update'
|
||||
editModel.value = true
|
||||
editType.value = 'edit'
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnDel': // 删除主表
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage({
|
||||
message: '请选择要删除的项',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
handleFirstDel(firstTemp)
|
||||
break
|
||||
case 'btnAddDetail': // 添加明细行
|
||||
handleAddOrderDetail()
|
||||
break
|
||||
case 'btnDelDetail': // 删除明细行
|
||||
if (multipleSelection.value.length < 1) {
|
||||
ElMessage({
|
||||
message: '至少删除一个',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
handleSecondDel(multipleSelection.value)
|
||||
break
|
||||
case 'btnExport':
|
||||
mainTable.value.exportExcel(`表结构${parseTime(new Date())}`, callback)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
//引入核心框架
|
||||
import { ElMessage, ElNotification } from 'element-plus'
|
||||
import { Plus, Delete, FullScreen, Close, Check, ArrowLeft } from '@element-plus/icons-vue'
|
||||
import { onMounted, ref, reactive, nextTick, computed, defineComponent, watch } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
// ------------------------主数据列表处理------------------------------------
|
||||
const getList = () => {
|
||||
listLoading.value = true
|
||||
{FirstTableName}s.getList(firstQuery).then(response => {
|
||||
response.columnFields.forEach(item => {
|
||||
// 首字母小写
|
||||
item.columnName =
|
||||
item.columnName.substring(0, 1).toLowerCase() +
|
||||
item.columnName.substring(1)
|
||||
//引入API,共用方法
|
||||
import * as {FirstTableName}s from '@/api/{FirstTableName}s'
|
||||
import * as {SecondTableName}s from '@/api/{SecondTableName}s'
|
||||
import { parseTime, defaultVal } from '@/utils/index'
|
||||
import common from "@/extensions/common.js"
|
||||
import { getItem } from '@/utils/storage'
|
||||
|
||||
//引入组件
|
||||
import Sticky from '@/components/Sticky/index.vue'
|
||||
import permissionBtn from '@/components/PermissionBtn/index.vue'
|
||||
import AuthForm from '../../components/Base/AuthForm.vue'
|
||||
import AuthTable from '../../components/Base/AuthTable.vue'
|
||||
import QueryBuilder from '@/components/QueryBuilder/index.vue'
|
||||
import ColumnSetting from '@/components/ColumnSetting/index.vue'
|
||||
|
||||
const firstHeaderList = ref([]) //列表头
|
||||
const secondHeaderList = ref([]) //明细列表头
|
||||
const multipleSelection = ref([]) //明细中checkbox选中的值
|
||||
const mainList = ref([]) //主列表值
|
||||
const secondList = ref([]) //明细列表值
|
||||
const firstTotal = ref(0) //主列表总条数
|
||||
const secondTotal = ref(0) //主列表总条数
|
||||
const listLoading = ref(true) //进度条
|
||||
const editModel = ref(false) //是否为编辑状态
|
||||
const editType = ref('edit')
|
||||
const dialogStatus = ref('') //主修改对话框状态create/update
|
||||
const radio = ref('') //主列表选项值
|
||||
const tableName = ref('')
|
||||
const showDetailInTable = ref(false) // 是否在主表位置显示详情
|
||||
let firstTemp = reactive({}) //当前选中的主表项
|
||||
let selectRow = reactive({}) //当前选中的主表项
|
||||
//组件refs
|
||||
const mainTable = ref(null)
|
||||
const dataForm = ref(null)
|
||||
const secondTable = ref(null)
|
||||
const preView = ref(null)
|
||||
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const defaultorgid = computed(() => store.getters.defaultorgid)
|
||||
const { showPreview, printPreview, createFlowInstance } = common()
|
||||
|
||||
const firstQuery = reactive({
|
||||
// 查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
key: undefined,
|
||||
})
|
||||
const secondQuery = reactive({
|
||||
// 明细查询条件
|
||||
page: 1,
|
||||
limit: 20,
|
||||
customerKey: undefined,
|
||||
})
|
||||
|
||||
const showNotFullScreen = ref(true) //明细列表是否全屏
|
||||
watch(showNotFullScreen, () => {
|
||||
secondQuery.limit = showNotFullScreen.value ? 10 : 20
|
||||
querySecondList(radio.value)
|
||||
})
|
||||
|
||||
const mainRules = reactive({
|
||||
Id: [
|
||||
{
|
||||
required: true,
|
||||
message: '入库通知单号不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
// ------------------------通用处理函数-------------------------------------
|
||||
const onBtnClicked = (domId, args, callback) => {
|
||||
switch (domId) {
|
||||
case 'btnAdd': // 添加
|
||||
resetFirstTemp()
|
||||
secondList.value = []
|
||||
dialogStatus.value = 'create'
|
||||
editModel.value = true
|
||||
tableName.value = '新建'
|
||||
editType.value = 'add'
|
||||
showDetailInTable.value = true // 显示详情编辑界面
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
firstHeaderList.value = response.columnFields
|
||||
mainList.value = response.data
|
||||
firstTotal.value = response.count
|
||||
if (firstTotal.value > 0) {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
break
|
||||
case 'btnEdit': // 编辑
|
||||
Object.assign(firstTemp, selectRow)
|
||||
if (firstTemp.id === '') {
|
||||
editModel.value = false
|
||||
ElMessage.error('请选择要修改的项')
|
||||
return
|
||||
}
|
||||
listLoading.value = false
|
||||
})
|
||||
}
|
||||
const rowClickFirstTable = function (row) {
|
||||
// 点击行
|
||||
radio.value = row.id
|
||||
tableName.value = row.tableName
|
||||
secondQuery.page = 1
|
||||
secondQuery.limit = 10
|
||||
querySecondList(radio.value)
|
||||
showTitleDetail(row)
|
||||
}
|
||||
const handleFilter = function () {
|
||||
firstQuery.page = 1
|
||||
getList()
|
||||
}
|
||||
const handleSizeChange = function (val) {
|
||||
firstQuery.limit = val
|
||||
getList()
|
||||
}
|
||||
const handleCurrentChange = function (val) {
|
||||
firstQuery.page = val
|
||||
getList()
|
||||
}
|
||||
const resetFirstTemp = function () {
|
||||
firstHeaderList.value.forEach(item => {
|
||||
firstTemp[item.columnName] = defaultVal(item.entityType)
|
||||
})
|
||||
firstTemp.namespace = 'OpenAuth.Repository.Domain'
|
||||
dialogStatus.value = 'update'
|
||||
editModel.value = true
|
||||
editType.value = 'edit'
|
||||
showDetailInTable.value = true // 编辑时显示详情界面
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
break
|
||||
case 'btnDel': // 删除
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage.error('请选择要删除的项')
|
||||
return
|
||||
}
|
||||
handleFirstDel(firstTemp)
|
||||
break
|
||||
case 'btnPrint':
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage.error('请选择要打印的内容')
|
||||
return
|
||||
}
|
||||
showPreview(preView.value, args, firstTemp);
|
||||
break
|
||||
case 'btnFlowscheme':
|
||||
if (firstTemp.id === '') {
|
||||
ElMessage.error('请选择要送审的内容')
|
||||
return
|
||||
}
|
||||
createFlowInstance(args, firstTemp);
|
||||
break
|
||||
case 'btnAddDetail': // 添加明细行
|
||||
handleAddOrderDetail()
|
||||
break
|
||||
case 'btnDelDetail': // 删除明细行
|
||||
if (multipleSelection.value.length < 1) {
|
||||
ElMessage.error('至少删除一个')
|
||||
return
|
||||
}
|
||||
handleSecondDel(multipleSelection.value)
|
||||
break
|
||||
case 'btnExport': //导出
|
||||
mainTable.value.exportExcel(`表结构${parseTime(new Date())}`, callback)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const setDetails = tempData => {
|
||||
// 处理明细
|
||||
tempData.{SecondTableName}Reqs = []
|
||||
secondList.value.length > 0 &&
|
||||
// ------------------------主数据列表处理------------------------------------
|
||||
|
||||
const getList = () => {
|
||||
listLoading.value = true
|
||||
{FirstTableName}s.getList(firstQuery).then(response => {
|
||||
response.columnFields.forEach(item => {
|
||||
// 首字母小写
|
||||
item.columnName =
|
||||
item.columnName.substring(0, 1).toLowerCase() +
|
||||
item.columnName.substring(1)
|
||||
})
|
||||
firstHeaderList.value = response.columnFields
|
||||
// 尝试从存储中加载列设置
|
||||
const savedSettings = getItem('{FirstTableName}s_column_settings')
|
||||
if (savedSettings) {
|
||||
// 将保存的设置合并到当前列表
|
||||
firstHeaderList.value = savedSettings
|
||||
}
|
||||
mainList.value = response.data
|
||||
firstTotal.value = response.count
|
||||
if (firstTotal.value > 0) {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
}
|
||||
listLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 处理列设置更新
|
||||
const handleColumnsUpdate = (columns) => {
|
||||
if (columns) {
|
||||
// 更新列显示配置
|
||||
firstHeaderList.value = columns
|
||||
} else {
|
||||
// 如果是null,则重置回默认配置
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
const updateSecondColumns = (columns) => {
|
||||
if (columns) {
|
||||
// 更新列显示配置
|
||||
secondHeaderList.value = columns
|
||||
} else {
|
||||
// 如果是null,则重置回默认配置
|
||||
querySecondList(radio.value)
|
||||
}
|
||||
}
|
||||
|
||||
const rowClickFirstTable = row => {
|
||||
// 点击行
|
||||
mainTable.value.clearSelection()
|
||||
mainTable.value.toggleRowSelection(row)
|
||||
|
||||
radio.value = row.id
|
||||
tableName.value = row.id
|
||||
secondQuery.page = 1
|
||||
secondQuery.limit = 10
|
||||
querySecondList(radio.value)
|
||||
showTitleDetail(row)
|
||||
}
|
||||
|
||||
const handleFilter = () => {
|
||||
firstQuery.page = 1
|
||||
getList()
|
||||
}
|
||||
const handleSizeChange = val => {
|
||||
firstQuery.limit = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = val => {
|
||||
firstQuery.page = val
|
||||
getList()
|
||||
}
|
||||
const resetFirstTemp = () => {
|
||||
firstHeaderList.value.forEach(item => {
|
||||
firstTemp[item.columnName] = defaultVal(item.entityType)
|
||||
})
|
||||
}
|
||||
const createData = () => {
|
||||
// 保存提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
tempData.OrgId = defaultorgid.value
|
||||
{FirstTableName}s.add(tempData).then(resp => {
|
||||
if (resp.result != undefined) {
|
||||
//如果服务器返回生成的ID
|
||||
firstTemp.id = resp.result
|
||||
}
|
||||
mainList.value.unshift(firstTemp)
|
||||
editModel.value = false
|
||||
showDetailInTable.value = false // 保存后返回列表视图
|
||||
rowClickFirstTable(firstTemp)
|
||||
ElNotification.success('创建成功')
|
||||
})
|
||||
})
|
||||
}
|
||||
const showTitleDetail = row => {
|
||||
// 弹出编辑框
|
||||
Object.assign(selectRow, row) // 新增订单时保存当前选中行
|
||||
Object.assign(firstTemp, row) // copy obj
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
}
|
||||
const setDetails = tempData => {
|
||||
// 处理明细
|
||||
tempData.{SecondTableName}Reqs = []
|
||||
secondList.value.length > 0 &&
|
||||
secondList.value.forEach(item => {
|
||||
const obj = {}
|
||||
|
||||
secondHeaderList.value.forEach(header => {
|
||||
obj[header.columnName] =
|
||||
item[header.columnName] || defaultVal(header.entityType)
|
||||
item[header.columnName] || defaultVal(header.entityType)
|
||||
})
|
||||
|
||||
tempData.{SecondTableName}Reqs.push(obj)
|
||||
})
|
||||
return tempData
|
||||
}
|
||||
|
||||
const createData = function () {
|
||||
// 保存提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
tempData.OrgId = defaultorgid.value
|
||||
{FirstTableName}s.add(tempData).then(resp => {
|
||||
tempData.id = resp.result
|
||||
mainList.value.unshift(tempData)
|
||||
editModel.value = false
|
||||
rowClickFirstTable(tempData)
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '创建成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const showTitleDetail = function (row) {
|
||||
// 弹出编辑框
|
||||
Object.assign(selectRow, row) // 新增订单时保存当前选中行
|
||||
Object.assign(firstTemp, row) // copy obj
|
||||
nextTick(() => {
|
||||
dataForm.value.clearValidate()
|
||||
})
|
||||
}
|
||||
const updateData = function () {
|
||||
// 更新提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of mainList.value) {
|
||||
if (v.id === firstTemp.id) {
|
||||
const index = mainList.value.indexOf(v)
|
||||
mainList.value.splice(index, 1, tempData)
|
||||
break
|
||||
}
|
||||
return tempData
|
||||
}
|
||||
const updateData = () => {
|
||||
// 更新提交
|
||||
dataForm.value.validate(() => {
|
||||
let tempData = Object.assign({}, firstTemp)
|
||||
tempData = setDetails(tempData)
|
||||
{FirstTableName}s.update(tempData).then(() => {
|
||||
for (const v of mainList.value) {
|
||||
if (v.id === firstTemp.id) {
|
||||
const index = mainList.value.indexOf(v)
|
||||
mainList.value.splice(index, 1, tempData)
|
||||
break
|
||||
}
|
||||
editModel.value = false
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const handleFirstDel = function (row) {
|
||||
// 删除头
|
||||
{FirstTableName}s.del([row.id]).then(() => {
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
mainList.value = mainList.value.filter(item => item.id !== row.id)
|
||||
if (mainList.value.length > 0) {
|
||||
nextTick(() => {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
})
|
||||
return
|
||||
}
|
||||
secondList.value = []
|
||||
showTitleDetail({})
|
||||
editModel.value = false
|
||||
showDetailInTable.value = false // 保存后返回列表视图
|
||||
ElNotification.success('更新成功')
|
||||
})
|
||||
}
|
||||
// ------------------------明细列表处理-------------------------------------
|
||||
const handleSecondPage = function (e) {
|
||||
secondQuery.page = e
|
||||
querySecondList(radio.value)
|
||||
}
|
||||
const querySecondList = id => {
|
||||
{SecondTableName}s
|
||||
.getList({
|
||||
{ParentTableId}: id,
|
||||
page: secondQuery.page,
|
||||
limit: secondQuery.limit,
|
||||
key: secondQuery.customerKey,
|
||||
})
|
||||
.then(res => {
|
||||
}
|
||||
const handleFirstDel = row => {
|
||||
// 主表删除处理
|
||||
{FirstTableName}s.del([row.id]).then(() => {
|
||||
ElNotification.success('删除成功')
|
||||
mainList.value = mainList.value.filter(item => item.id !== row.id)
|
||||
if (mainList.value.length > 0) {
|
||||
nextTick(() => {
|
||||
rowClickFirstTable(mainList.value[0])
|
||||
})
|
||||
return
|
||||
}
|
||||
secondList.value = []
|
||||
showTitleDetail({})
|
||||
})
|
||||
}
|
||||
|
||||
//返回列表
|
||||
const returnToList = () => {
|
||||
showDetailInTable.value = false
|
||||
cancelEdit()
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = () => {
|
||||
editModel.value = false
|
||||
if (dialogStatus.value === 'create') {
|
||||
showDetailInTable.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 主列表双击行事件
|
||||
const rowDblClickFirstTable = row => {
|
||||
rowClickFirstTable(row) // 先执行单击行的逻辑
|
||||
showDetailInTable.value = true // 在主表位置显示详情
|
||||
}
|
||||
|
||||
// ------------------------明细列表处理-------------------------------------
|
||||
const handleSecondPage = e => {
|
||||
secondQuery.page = e
|
||||
querySecondList(radio.value)
|
||||
}
|
||||
|
||||
const querySecondList = id => {
|
||||
{SecondTableName}s
|
||||
.getList({
|
||||
{ParentTableId}: id,
|
||||
page: secondQuery.page,
|
||||
limit: secondQuery.limit,
|
||||
key: secondQuery.customerKey,
|
||||
})
|
||||
.then(res => {
|
||||
res.columnFields.forEach(item => {
|
||||
// 首字母小写
|
||||
item.columnName =
|
||||
item.columnName.substring(0, 1).toLowerCase() +
|
||||
item.columnName.substring(1)
|
||||
item.columnName.substring(0, 1).toLowerCase() +
|
||||
item.columnName.substring(1)
|
||||
})
|
||||
secondHeaderList.value = res.columnFields
|
||||
// 尝试从存储中加载列设置
|
||||
const savedSettings = getItem('{SecondTableName}s_column_settings')
|
||||
if (savedSettings) {
|
||||
// 将保存的设置合并到当前列表
|
||||
secondHeaderList.value = savedSettings
|
||||
}
|
||||
secondTotal.value = res.count
|
||||
secondList.value = res.data
|
||||
})
|
||||
}
|
||||
const rowClickSecondTable = function (row) {
|
||||
// 行点击事件
|
||||
secondTable.value.clearSelection()
|
||||
secondTable.value.toggleRowSelection(row)
|
||||
}
|
||||
const handleSecondDel = function (rows) {
|
||||
{SecondTableName}s.del(rows.map(item => item.id)).then(() => {
|
||||
rows.forEach(row => {
|
||||
secondList.value = secondList.value.filter(item => item.id !== row.id)
|
||||
})
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
}
|
||||
const rowClickSecondTable = row => {
|
||||
// 行点击事件
|
||||
secondTable.value.clearSelection()
|
||||
secondTable.value.toggleRowSelection(row)
|
||||
}
|
||||
const handleSecondDel = function (rows) {
|
||||
{SecondTableName}s.del(rows.map(item => item.id)).then(() => {
|
||||
rows.forEach(row => {
|
||||
secondList.value = secondList.value.filter(item => item.id !== row.id)
|
||||
})
|
||||
}
|
||||
const selChangeSecondTable = function (val) {
|
||||
// 明细选中事件
|
||||
multipleSelection.value = val
|
||||
}
|
||||
const handleUpdateDetail = function (item) {
|
||||
// 同步表数据结构
|
||||
{SecondTableName}s.update(item).then(() => {
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
}
|
||||
ElNotification.success('删除成功')
|
||||
})
|
||||
}
|
||||
const selChangeSecondTable = val => {
|
||||
// 明细选中事件
|
||||
multipleSelection.value = val
|
||||
}
|
||||
const handleAddOrderDetail = () => {
|
||||
// 添加明细
|
||||
const obj = {}
|
||||
secondHeaderList.value.forEach(header => {
|
||||
obj[header.columnName] = defaultVal(header.entityType)
|
||||
})
|
||||
obj.orderId = firstTemp.id
|
||||
|
||||
secondList.value.push(Object.assign({}, obj))
|
||||
}
|
||||
const handleUpdateDetail = item => {
|
||||
// 回车保存明细,如果不需要可以不用
|
||||
{SecondTableName}s.update(item).then(() => {
|
||||
ElNotification.success('成功')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -32,6 +32,10 @@ namespace OpenAuth.App
|
||||
{
|
||||
objs = objs.Where(u => u.Name.Contains(request.key));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.sqlWhere))
|
||||
{
|
||||
objs = objs.Where(request.sqlWhere);
|
||||
}
|
||||
|
||||
{ForeignKeyTemplate}
|
||||
|
||||
@@ -42,29 +46,26 @@ namespace OpenAuth.App
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(AddOrUpdate{ClassName}Req req)
|
||||
public void Add(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var obj = req.MapTo<{ClassName}>();
|
||||
obj.Id = Guid.NewGuid().ToString();
|
||||
//todo: 根据业务需要调整字段
|
||||
obj.Createtime = DateTime.Now;
|
||||
var obj = request.MapTo<{ClassName}>();
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.Createuserid = user.Id;
|
||||
obj.Createusername = user.Name;
|
||||
//todo:补充或调整自己需要的字段
|
||||
{InsertColumns}
|
||||
if(obj.KeyIsNull()) //如果主键为空,则生成默认值
|
||||
{
|
||||
obj.GenerateDefaultKeyVal();
|
||||
}
|
||||
Repository.Insert(obj);
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdate{ClassName}Req obj)
|
||||
public void Update(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
Repository.Update(u => new {ClassName}
|
||||
{
|
||||
//todo: 根据业务需要调整字段
|
||||
{ClassName} = obj.{ClassName},
|
||||
Updatetime = DateTime.Now,
|
||||
Updateuserid = user.Id,
|
||||
Updateusername = user.Name
|
||||
},u => u.Id == obj.Id);
|
||||
{UpdateColumns}
|
||||
},u => u.Id == request.Id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -52,29 +52,27 @@ namespace OpenAuth.App
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(AddOrUpdate{ClassName}Req req)
|
||||
public void Add(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var obj = req.MapTo<{ClassName}>();
|
||||
obj.Id = Guid.NewGuid().ToString();
|
||||
//todo: 根据业务需要调整字段
|
||||
obj.Createtime = DateTime.Now;
|
||||
var obj = request.MapTo<{ClassName}>();
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
obj.Createuserid = user.Id;
|
||||
obj.Createusername = user.Name;
|
||||
//todo:补充或调整自己需要的字段
|
||||
{InsertColumns}
|
||||
if(obj.KeyIsNull()) //如果主键为空,则生成默认值
|
||||
{
|
||||
obj.GenerateDefaultKeyVal();
|
||||
}
|
||||
Repository.Insert(obj);
|
||||
}
|
||||
|
||||
public void Update(AddOrUpdate{ClassName}Req obj)
|
||||
public void Update(AddOrUpdate{ClassName}Req request)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
Repository.Update(u => new {ClassName}
|
||||
{
|
||||
//todo: 根据业务需要调整字段
|
||||
Name = obj.Name,
|
||||
Updatetime = DateTime.Now,
|
||||
Updateuserid = user.Id,
|
||||
Updateusername = user.Name
|
||||
},u => u.Id == obj.Id);
|
||||
{UpdateColumns}
|
||||
},u => u.Id == request.Id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
<div class="flex flex-column">
|
||||
<sticky>
|
||||
<div class="search-box">
|
||||
<el-input @keyup.enter="handleFilter" size="small" class="custom-input w-200" :placeholder="'名称'"
|
||||
v-model="listQuery.key"></el-input>
|
||||
<el-button size="small" class="custom-button filter-item" icon="el-icon-search"
|
||||
@click="handleFilter">搜索</el-button>
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{TableName}_query_conditions" :columns="headerList" :query-options="listQuery" @search="handleFilter" class="flex-1" />
|
||||
<column-setting storageName="{TableName}_column_settings" :columns="headerList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
</sticky>
|
||||
@@ -59,6 +60,8 @@ const listLoading = ref(true) //进度条
|
||||
const dialogFormVisible = ref(false) //是否显示编辑框
|
||||
const dialogStatus = ref(0) //0:新增 1:编辑
|
||||
let temp = reactive({}) //新增(编辑)绑定对话框
|
||||
import ColumnSetting from '@/components/ColumnSetting/index.vue'
|
||||
import QueryBuilder from '@/components/QueryBuilder/index.vue'
|
||||
const listQuery = reactive({
|
||||
// 查询条件
|
||||
page: 1,
|
||||
@@ -71,12 +74,37 @@ const rules = reactive({
|
||||
//组件refs
|
||||
const mainTableRef = ref(null)
|
||||
const dataFormRef = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
initHeaderList()
|
||||
getList()
|
||||
})
|
||||
|
||||
// 初始化表头
|
||||
const initHeaderList = () => {
|
||||
headerList.value = [
|
||||
{HeaderList}
|
||||
]
|
||||
getList()
|
||||
})
|
||||
|
||||
// 尝试从存储中加载列设置
|
||||
const savedSettings = getItem('{TableName}_column_settings')
|
||||
if (savedSettings) {
|
||||
// 将保存的设置合并到当前列表
|
||||
headerList.value = savedSettings
|
||||
}
|
||||
}
|
||||
|
||||
// 处理列设置更新
|
||||
const handleColumnsUpdate = (columns) => {
|
||||
if (columns) {
|
||||
// 更新列显示配置
|
||||
headerList.value = columns
|
||||
} else {
|
||||
// 如果是null,则重置回默认配置
|
||||
initHeaderList()
|
||||
}
|
||||
}
|
||||
|
||||
const rowClick = function (row) {
|
||||
mainTableRef.value.clearSelection()
|
||||
mainTableRef.value.toggleRowSelection(row)
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
<div class="flex flex-column">
|
||||
<sticky>
|
||||
<div class="search-box">
|
||||
<el-input @keyup.enter="handleFilter" size="small" class="custom-input w-200" :placeholder="'名称'"
|
||||
v-model="listQuery.key"></el-input>
|
||||
<el-button size="small" class="custom-button filter-item" icon="el-icon-search"
|
||||
@click="handleFilter">搜索</el-button>
|
||||
<div class="flex items-center">
|
||||
<query-builder storageName="{TableName}_query_conditions" :columns="headerList" :query-options="listQuery" @search="handleFilter" class="flex-1" />
|
||||
<column-setting storageName="{TableName}_column_settings" :columns="headerList"
|
||||
@update:columns="handleColumnsUpdate" class="ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
|
||||
</sticky>
|
||||
@@ -59,6 +60,8 @@ const listLoading = ref(true) //进度条
|
||||
const dialogFormVisible = ref(false) //是否显示编辑框
|
||||
const dialogStatus = ref(0) //0:新增 1:编辑
|
||||
let temp = reactive({}) //新增(编辑)绑定对话框
|
||||
import ColumnSetting from '@/components/ColumnSetting/index.vue'
|
||||
import QueryBuilder from '@/components/QueryBuilder/index.vue'
|
||||
const listQuery = reactive({
|
||||
// 查询条件
|
||||
page: 1,
|
||||
@@ -118,10 +121,29 @@ const getList = function () {
|
||||
item.columnName.substring(1)
|
||||
})
|
||||
headerList.value = response.columnFields
|
||||
|
||||
// 尝试从存储中加载列设置
|
||||
const savedSettings = getItem('{TableName}_column_settings')
|
||||
if (savedSettings) {
|
||||
// 将保存的设置合并到当前列表
|
||||
headerList.value = savedSettings
|
||||
}
|
||||
total.value = response.count
|
||||
listLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 处理列设置更新
|
||||
const handleColumnsUpdate = (columns) => {
|
||||
if (columns) {
|
||||
// 更新列显示配置
|
||||
headerList.value = columns
|
||||
} else {
|
||||
// 如果是null,则重置回默认配置
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
const handleFilter = function () {
|
||||
listQuery.page = 1
|
||||
getList()
|
||||
|
||||
25
README.md
25
README.md
@@ -1,6 +1,8 @@
|
||||
🔥.Net权限管理及快速开发框架、最好用的权限工作流系统。源于Martin Fowler企业级应用开发思想及最新技术组合(SqlSugar、EF、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、Vue2/3、Element-ui/plus、IdentityServer等)。核心模块包括:角色授权、代码生成、API鉴权、智能打印、表单设计、工作流、定时任务等。架构易扩展,是中小企业的首选。
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img src="https://gitee.com/uploads/images/2018/0425/163228_7077c3fd_362401.png" alt="LOGO" width="300" />
|
||||
</p>
|
||||
|
||||
**logo图标含义** OpenAuth中OA字母的结合体;整体像鱼,授人以渔;你非说像咸鱼,那也是积极向上的咸鱼;中心是个笑脸,微笑面对生活(✿◡‿◡)。
|
||||
|
||||
@@ -36,9 +38,9 @@
|
||||
|
||||
* Vue2演示地址: http://demo.openauth.net.cn:1803
|
||||
|
||||
* 移动H5演示地址(请使用浏览器移动模式或直接用手机打开): http://demo.openauth.net.cn:1804
|
||||
* 移动H5演示地址:http://demo.openauth.net.cn:1804
|
||||
|
||||
* 开源版演示地址: http://demo.openauth.net.cn:1802
|
||||
* 开源版演示地址:http://demo.openauth.net.cn:1802
|
||||
|
||||
## 📺视频教程
|
||||
|
||||
@@ -52,12 +54,15 @@
|
||||
|
||||
[OpenAuth.Net视频合集--表单设计](https://www.bilibili.com/video/BV1dagEeFEVA/)
|
||||
|
||||
[OpenAuth.Net视频合集--多表代码生成](https://www.bilibili.com/video/BV1r4J3zAEsx/)
|
||||
|
||||
|
||||
## 📸效果预览
|
||||
#### 后端启动效果
|
||||

|
||||
|
||||
#### vue版本效果
|
||||

|
||||

|
||||

|
||||

|
||||
@@ -87,17 +92,17 @@
|
||||
|
||||
* 超强的自定义权限控制功能,请参考:[通用权限设计与实现](https://www.cnblogs.com/yubaolee/p/DataPrivilege.html)
|
||||
|
||||
* 完整API鉴权,可以控制角色可访问的API资源,及模块功能字段可见及是否返回,请参考:[按角色授权API资源](http://doc.openauth.net.cn/core/apiauth.html#%E6%8C%89%E8%A7%92%E8%89%B2%E6%8E%88%E6%9D%83api%E8%B5%84%E6%BA%90) 及 [字段权限](http://doc.openauth.net.cn/core/datapropertyrule.html)
|
||||
* 完整API鉴权,可以控制角色可访问的API资源,及模块功能字段可见及是否返回,请参考:[按角色授权API资源](http://doc.openauth.net.cn/core/apiauth.html#%E6%8C%89%E8%A7%92%E8%89%B2%E6%8E%88%E6%9D%83api%E8%B5%84%E6%BA%90) 及 [字段权限](http://doc.openauth.net.cn/core/datapropertyrule/)
|
||||
|
||||
* 可拖拽的表单设计。详情:[可拖拽表单](http://doc.openauth.net.cn/pro/dragform.html)
|
||||
* 可拖拽的表单设计。详情:[可拖拽表单](http://doc.openauth.net.cn/pro/dragform/)
|
||||
|
||||
* 可视化流程设计。[可视化流程设计](http://doc.openauth.net.cn/pro/startflow.html)
|
||||
* 可视化流程设计。[可视化流程设计](http://doc.openauth.net.cn/pro/startflow/)
|
||||
|
||||
* 全网最好用的打印解决方案。详情:[智能打印](http://doc.openauth.net.cn/pro/printerplan.html)
|
||||
* 全网最好用的打印解决方案。详情:[智能打印](http://doc.openauth.net.cn/pro/printerplan/)
|
||||
|
||||
* 基于Quartz.Net的定时任务控制,可随时启/停,可视化配置Cron表达式功能,请参考:[定时任务](http://doc.openauth.net.cn/core/job.html)
|
||||
* 基于Quartz.Net的定时任务控制,可随时启/停,可视化配置Cron表达式功能,请参考:[定时任务](http://doc.openauth.net.cn/core/job/)
|
||||
|
||||
* 零代码动态API接口,详情:[动态API](http://doc.openauth.net.cn/core/dynamicapi.html)
|
||||
* 零代码动态API接口,详情:[动态API](http://doc.openauth.net.cn/core/dynamicapi/)
|
||||
|
||||
* 基于CodeSmith的代码生成功能,可快速生成带有头/明细结构的页面
|
||||
|
||||
@@ -107,7 +112,7 @@
|
||||
|
||||
* 支持多租户
|
||||
|
||||
* 支持搭建自己的IdentityServer服务器,实现基于OAuth2的登录体系,请参考:[登录认证及OAuth集成](http://doc.openauth.net.cn/core/identity.html)
|
||||
* 支持搭建自己的IdentityServer服务器,实现基于OAuth2的登录体系,请参考:[登录认证及OAuth集成](http://doc.openauth.net.cn/core/identity/)
|
||||
|
||||
* 建立三方对接规范,已有系统可以无缝对接流程引擎
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
|
||||
* @Date: 2025-04-23 20:26:48
|
||||
* @LastEditTime: 2025-05-07 20:49:08
|
||||
* @LastEditTime: 2025-05-17 21:19:46
|
||||
* @Description: 笔记配置
|
||||
* Copyright (c) 2025 by yubaolee | ahfu~ , All Rights Reserved.
|
||||
*/
|
||||
@@ -54,12 +54,12 @@ const proNote = defineNoteConfig({
|
||||
{
|
||||
text: '基础开发',
|
||||
collapsed: false,
|
||||
items: ['start','deploy','structure','devnew', 'router','openurl','keepalive','datapropertyrule']
|
||||
items: ['start','deploy','structure','devnew','devnewmulti', 'router','openurl','keepalive','datapropertyrule']
|
||||
},
|
||||
{
|
||||
text: '内置组件',
|
||||
collapsed: false,
|
||||
items: ['table','querybuilder','columnsetting','components']
|
||||
items: ['table','querybuilder','columnsetting','authselect','authform']
|
||||
},
|
||||
'printerplan',
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ OpenAuth.Net工作流基于国际标准的BPMN2.0规范,并在此基础上做
|
||||
|
||||
4. 流程审批:【流程中心/待处理流程】中进行审批;
|
||||
|
||||
这里只介绍开源版本的操作流程,vue版本操作流程请参考👉[操作手册](/pro/startflow.md)
|
||||
这里只介绍开源版本的操作流程,vue版本操作流程请参考👉[操作手册](/pro/startflow/)
|
||||
|
||||
## 流程设计
|
||||
|
||||
@@ -63,13 +63,13 @@ OpenAuth.Net工作流基于国际标准的BPMN2.0规范,并在此基础上做
|
||||
* 指定用户:指定用户审批,当选中这项时需要选择指定的用户
|
||||
* 指定角色:指定角色审批,当选中这项时需要选择指定的角色
|
||||
|
||||
👉使用[vue版本](/pro/startflow),解锁更多类型:指定SQL、上一节点执行人的直属上级、连续多级直属上级、部门负责人、运行时指定角色、运行时指定用户等等,当然你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,添加更多类型。
|
||||
👉使用[vue版本](/pro/startflow/),解锁更多类型:指定SQL、上一节点执行人的直属上级、连续多级直属上级、部门负责人、运行时指定角色、运行时指定用户等等,当然你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,添加更多类型。
|
||||
|
||||
👉使用[vue版本](/pro/startflow),设置连线条件属性。你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,让它支持该属性。
|
||||
👉使用[vue版本](/pro/startflow/),设置连线条件属性。你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,让它支持该属性。
|
||||
|
||||
## 流程发起
|
||||
|
||||
开源版本只能发起无业务关联的流程,使用[vue版本](/pro/startflow),解锁有业务类型关联的流程。当然你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,支持更多类型。
|
||||
开源版本只能发起无业务关联的流程,使用[vue版本](/pro/startflow/),解锁有业务类型关联的流程。当然你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,支持更多类型。
|
||||
|
||||
### 无业务关联流程
|
||||
|
||||
@@ -97,7 +97,7 @@ OpenAuth.Net工作流基于国际标准的BPMN2.0规范,并在此基础上做
|
||||
|
||||
* 驳回:可以驳回到指定的步骤,该步骤的人可以重新审批提交;
|
||||
|
||||
👉使用[vue版本](/pro/startflow),解锁更多加签、撤销审批操作。你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,让它支持这些功能。
|
||||
👉使用[vue版本](/pro/startflow/),解锁更多加签、撤销审批操作。你也可以为[开源版本](https://gitee.com/dotnetchina/OpenAuth.Net)贡献代码,让它支持这些功能。
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -183,10 +183,7 @@ stop
|
||||
|
||||
### ActivityId: 当前活动节点,即待审批的节点
|
||||
|
||||
与流程实例密切相关的还有两个表:流程实例的操作记录FlowInstanceOperationHistory及流转记录FlowInstanceTransitionHistory。它们有不同的作用:
|
||||
与流程实例密切相关的还有:流程实例的操作记录FlowInstanceOperationHistory。
|
||||
|
||||
## 操作记录FlowInstanceOperationHistory
|
||||
该表记录了流程实例的所有操作记录,包括流程的创建、撤回、驳回、同意、不同意等操作。
|
||||
|
||||
## 流转记录FlowInstanceTransitionHistory
|
||||
记录某个流程实例所有已审批的从一个活动节点到下一个活动节点的操作人、操作时间。
|
||||
|
||||
@@ -74,3 +74,45 @@ public class ResourceApp:SqlSugarBaseApp<Resource>
|
||||
}
|
||||
```
|
||||
|
||||
## 事务
|
||||
|
||||
SqlSugar提供了强大的事务功能,在OpenAuth.Net中,只需要在业务开始时,调用`SugarClient.Ado.BeginTran()`开启事务,在业务结束时,调用`SugarClient.Ado.CommitTran()`提交事务,即可使用事务。
|
||||
|
||||
比如一个复杂的主从表业务,无论中间过程使用的是SugarClient还是Repository,都可以用这种方式来实现事务:
|
||||
```csharp
|
||||
SugarClient.Ado.BeginTran();
|
||||
|
||||
// 更新从表中的字段
|
||||
if (obj.SubTableReqs != null && obj.SubTableReqs.Any())
|
||||
{
|
||||
//id比数据库少的,删除
|
||||
var containids = obj.SubTableReqs.Select(u => u.Id)
|
||||
.Where(u => !string.IsNullOrEmpty(u)).ToList();
|
||||
if (containids.Any())
|
||||
{
|
||||
SugarClient.Deleteable<SubTable>(u => !containids.Contains(u.Id) && u.MainTableId == obj.Id).ExecuteCommand();
|
||||
}
|
||||
|
||||
//id为空时添加
|
||||
foreach (var detail in obj.SubTableReqs.Where(u => string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
_subTableApp.Add(detail);
|
||||
}
|
||||
|
||||
//更新id相同的
|
||||
foreach (var detail in obj.SubTableReqs.Where(u => !string.IsNullOrEmpty(u.Id)))
|
||||
{
|
||||
_subTableApp.Update(detail);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新主表中的字段
|
||||
Repository.Update(u => new MainTable
|
||||
{
|
||||
NeedUpdateField = obj.NeedUpdateField,
|
||||
//todo:补充或调整自己需要的字段
|
||||
}, u => u.Id == obj.Id);
|
||||
|
||||
SugarClient.Ado.CommitTran();
|
||||
|
||||
```
|
||||
|
||||
125
newdocs/docs/notes/pro/authform.md
Normal file
125
newdocs/docs/notes/pro/authform.md
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: 通用表单组件
|
||||
createTime: 2025/04/23 23:43:26
|
||||
permalink: /pro/authform/
|
||||
---
|
||||
|
||||
通用表单组件`AuthForm`是一个通用的表单组件,支持多种表单控件类型,具有字段联动功能,能满足大部分表单场景需求。
|
||||
|
||||
## 基本用法
|
||||
|
||||
```vue
|
||||
<auth-form ref="dataForm" :edit-model="editModel" :form-fields="firstHeaderList" :data="firstTemp" :col-num="3"></auth-form>
|
||||
```
|
||||
|
||||
## 属性
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| editModel | Boolean | false | 是否可编辑 |
|
||||
| colNum | Number | 1 | 表单列数 |
|
||||
| data | Object | - | 表单绑定的数据对象 |
|
||||
| formFields | Array | [] | 表单字段定义 |
|
||||
| rules | Object | - | 表单验证规则 |
|
||||
| fieldRelations | Array | [] | 字段联动关系配置 |
|
||||
|
||||
## 事件
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| update:data | 表单数据更新时触发 | 更新后的表单数据对象 |
|
||||
| field-change | 表单字段值变化时触发 | `{ fieldName, value }` |
|
||||
|
||||
## 方法
|
||||
|
||||
| 方法名 | 说明 | 参数 |
|
||||
| --- | --- | --- |
|
||||
| validate | 表单验证 | callback: 验证成功时的回调函数 |
|
||||
| clearValidate | 清除验证信息 | - |
|
||||
| applyFieldRelations | 手动应用字段联动关系 | fieldName: 字段名, value: 字段值 |
|
||||
| handleFieldChange | 手动触发字段值变化 | fieldName: 字段名, value: 字段值 |
|
||||
|
||||
## formFields 字段配置
|
||||
|
||||
表单属性定义。为一个`ColumnDefine`的数组。
|
||||
|
||||
```javascript
|
||||
{
|
||||
columnName: 'fieldName', // 字段名,必填
|
||||
remark: '显示名称', // 字段显示名称,必填
|
||||
isEdit: true, // 是否在表单中显示,必填
|
||||
editType: 'input', // 编辑类型,必填
|
||||
sort: 1, // 排序值,越大越靠前
|
||||
dataSource: [] // 下拉选项数据源,当editType为select或selectDynamic时必填
|
||||
}
|
||||
```
|
||||
|
||||
### 支持的编辑类型 (editType)
|
||||
|
||||
| 类型 | 说明 |
|
||||
| --- | --- |
|
||||
| input | 普通文本输入框 |
|
||||
| textarea | 多行文本输入框 |
|
||||
| select | 静态下拉选择框 |
|
||||
| selectDynamic | 动态下拉选择框 |
|
||||
| date | 日期选择器 |
|
||||
| datetime | 日期时间选择器 |
|
||||
| switch | 开关控件 |
|
||||
| checkbox | 复选框 |
|
||||
| radio | 单选框 |
|
||||
| file | 文件上传 |
|
||||
|
||||
## 字段联动配置
|
||||
|
||||
fieldRelations 属性用于配置字段间的联动关系,每个联动关系对象结构如下:
|
||||
|
||||
```javascript
|
||||
{
|
||||
sourceField: 'fieldName', // 源字段名称
|
||||
targetField: 'targetFieldName', // 目标字段名称
|
||||
condition: { // 触发条件
|
||||
type: 'equals', // 条件类型
|
||||
value: 'someValue' // 条件值
|
||||
},
|
||||
actionType: 'setValue', // 联动动作类型
|
||||
value: 'newValue', // 设置的新值
|
||||
elseAction: { // 可选,条件不满足时的动作
|
||||
actionType: 'setValue',
|
||||
value: 'defaultValue'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 支持的条件类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
| --- | --- |
|
||||
| equals | 等于 |
|
||||
| notEquals | 不等于 |
|
||||
| contains | 包含 |
|
||||
| greaterThan | 大于 |
|
||||
| lessThan | 小于 |
|
||||
| empty | 为空 |
|
||||
| notEmpty | 不为空 |
|
||||
| custom | 自定义条件 |
|
||||
|
||||
### 支持的动作类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
| --- | --- |
|
||||
| setValue | 设置字段值 |
|
||||
| setVisible | 设置字段可见性 |
|
||||
| setDisabled | 设置字段是否禁用 |
|
||||
| clearValue | 清空字段值 |
|
||||
| setDataSource | 设置数据源 |
|
||||
|
||||
## 文件上传
|
||||
|
||||
组件支持文件上传功能,上传的文件将被发送到 `/Files/Upload` 接口。上传成功后,文件路径会被自动设置到对应的表单字段中。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 字段联动功能依赖于正确配置的fieldRelations属性
|
||||
2. 文件上传功能需要后端提供正确的接口支持
|
||||
3. 动态数据源需要配合AuthSelect组件使用
|
||||
4. 编辑模式(editModel)设置为false时,所有表单项将被禁用
|
||||
145
newdocs/docs/notes/pro/authselect.md
Normal file
145
newdocs/docs/notes/pro/authselect.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: 通用下拉组件
|
||||
createTime: 2025/05/12 10:24:28
|
||||
permalink: /pro/authselect/
|
||||
---
|
||||
|
||||
通用下拉选择`AuthSelect`用于渲染和编辑字典结构和动态列表返回key-valuel格式的数据。该组件与其他组件的关系如下:
|
||||

|
||||
|
||||
## 基本用法
|
||||
|
||||
下拉选择组件目前支持`静态字典数据`和`动态接口数据`两种模式。
|
||||
|
||||
### 字典数据模式
|
||||
静态字典。根据【字典分类】模块的`分类标识`关键字获取,如:
|
||||
```vue
|
||||
<auth-select
|
||||
:isEdit="true"
|
||||
@change="handleChange"
|
||||
:data-source="'SYS_STATUS'"
|
||||
v-model="value">
|
||||
</auth-select>
|
||||
```
|
||||
|
||||
### 动态接口模式
|
||||
动态列表。从指定Url获取value/label结构的数据进行渲染,如:
|
||||
```vue
|
||||
<auth-select
|
||||
:isEdit="true"
|
||||
@change="handleChange"
|
||||
:type="'dynamic'"
|
||||
:data-source="'/CategoryTypes/Load'"
|
||||
v-model="value">
|
||||
</auth-select>
|
||||
```
|
||||
|
||||
### 直接传入数组
|
||||
|
||||
```vue
|
||||
<auth-select
|
||||
:isEdit="true"
|
||||
@change="handleChange"
|
||||
:data-source="[{label: '选项1', value: '1'}, {label: '选项2', value: '2'}]"
|
||||
v-model="value">
|
||||
</auth-select>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| type | 下拉框类型,`static`表示从数据字典加载,`dynamic`表示从接口获取 | String | `static` |
|
||||
| dataSource | 数据源,当type=='dynamic'时,该值为一个Url地址,该地址返回一个id/name结构的数组。当type=='static'或空时,该值为【字典分类】模块的`分类标识` | String/Number/Array | - |
|
||||
| value / v-model | 绑定值 | String/Number | `''` |
|
||||
| isEdit | 是否为编辑状态,`false`时显示文本 | Boolean | `true` |
|
||||
| disabled | 是否禁用 | Boolean | `false` |
|
||||
| size | 输入框尺寸 | String | `default` |
|
||||
| searchKey | 搜索参数,当`type`为`dynamic`时有效 | Object | `{}` |
|
||||
| defaultProps | 自定义选项的值和标签字段。如果该值为空,则v-model需要传字段id/name的数组 | Object | `{label: 'label', value: 'value'}` |
|
||||
|
||||
### Events
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| change | 选中值发生变化时触发 | 选中项的值 |
|
||||
| update:value | 更新v-model值 | 选中项的值 |
|
||||
|
||||
## 示例
|
||||
|
||||
### 基础用法 - 字典数据
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<auth-select
|
||||
:isEdit="isEdit"
|
||||
@change="onChange"
|
||||
:data-source="'SYS_STATUS'"
|
||||
v-model="status">
|
||||
</auth-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const isEdit = ref(true)
|
||||
const status = ref('')
|
||||
|
||||
const onChange = (val) => {
|
||||
console.log('选中值:', val)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 动态接口数据
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<auth-select
|
||||
:isEdit="isEdit"
|
||||
@change="onChange"
|
||||
:type="'dynamic'"
|
||||
:data-source="'/CategoryTypes/Load'"
|
||||
:search-key="searchParams"
|
||||
v-model="selectedValue">
|
||||
</auth-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
const isEdit = ref(true)
|
||||
const selectedValue = ref('')
|
||||
const searchParams = reactive({
|
||||
key: 'value',
|
||||
otherParam: 'test'
|
||||
})
|
||||
|
||||
const onChange = (val) => {
|
||||
console.log('选中值:', val)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 自定义字段名
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<auth-select
|
||||
:isEdit="isEdit"
|
||||
:type="'dynamic'"
|
||||
:data-source="'/api/users'"
|
||||
:default-props="{label: 'username', value: 'id'}"
|
||||
v-model="userId">
|
||||
</auth-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const isEdit = ref(true)
|
||||
const userId = ref('')
|
||||
</script>
|
||||
```
|
||||
@@ -4,9 +4,7 @@ createTime: 2025/05/07 20:24:28
|
||||
permalink: /pro/columnsetting/
|
||||
---
|
||||
|
||||
## 功能简介
|
||||
|
||||
`ColumnSetting` 是一个用于表格列显示与顺序自定义的弹窗组件,支持列的拖拽排序、显示/隐藏切换,并将用户设置持久化到本地存储。用于配合框架el-table及[通用表格组件](./table.md)的列设置。
|
||||
列设置组件`ColumnSetting` 是一个用于表格列显示与顺序自定义的弹窗组件,支持列的拖拽排序、显示/隐藏切换,并将用户设置持久化到本地存储。用于配合框架el-table及[通用表格组件](./table.md)的列设置。
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
title: 其他组件
|
||||
createTime: 2025/04/23 23:43:26
|
||||
permalink: /pro/components/
|
||||
---
|
||||
|
||||
OpenAuth.Pro封装了一些组件,方便开发使用,组件全部在`src\components`中定义。目前提供的三大核心组件关系如下:
|
||||
|
||||

|
||||
|
||||
## 通用下拉选择 auth-select
|
||||
|
||||
通用下拉选择`auth-select`组件是用于渲染和编辑字典结构和动态列表返回key-valuel数据的组件。用法如下:
|
||||
|
||||
静态字典。根据【字典分类】模块的`分类标识`关键字获取,如:
|
||||
```html
|
||||
<auth-select :isEdit="isEdit" @change="change" :data-source="'SYS_STATUS'" v-model="val" size="small"></auth-select>
|
||||
```
|
||||
动态列表。从指定Url获取value/label结构的数据进行渲染,如:
|
||||
```html
|
||||
<auth-select :default-props="{label:'name', value:'id'}" :isEdit="isEdit" @change="change" :type="'dynamic'" :data-source="'/CategoryTypes/Load'" v-model="val" size="small"></auth-select>
|
||||
```
|
||||
|
||||
该组件有以下几个参数:
|
||||
|
||||
isEdit:是否编辑,如果为false则展示为label形式,如果为true则为编辑状态;
|
||||
|
||||
type:选择类型。'dynamic'表示动态加载,空或'static'则从系统Category表中获取;
|
||||
|
||||
data-source:数据源。当type=='dynamic'时,该值为一个Url地址,该地址返回一个id/name结构的数组。当type=='static'或空时,该值为【字典分类】模块的`分类标识`;
|
||||
|
||||
default-props: 数据源提供的数据类型。默认情况下为id/name的数组。如果设置该值,则需要提供对应结构的数据,如::default-props="{label:'tableName', value:'id'}",则提供的数据格式如下:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{id:'1',tableName:'category'},
|
||||
{id:'2',tableName:'user'},
|
||||
{id:'3',tableName:'module'},
|
||||
....
|
||||
]
|
||||
```
|
||||
|
||||
## 表单组件 auth-form
|
||||
|
||||
根据定义动态渲染表单项,减少表单的开发。用法如下:
|
||||
```html
|
||||
<auth-form ref="dataForm" :edit-model="editModel" :form-fields="firstHeaderList" :data="firstTemp" :col-num="3"></auth-form>
|
||||
```
|
||||
|
||||
当父组件需要使用表单里面的数据,进行提交操作时,参考以下代码:
|
||||
|
||||
```javascript
|
||||
submit() {
|
||||
// 保存提交
|
||||
this.$refs['dataForm'].validate(() => {
|
||||
let tempData = Object.assign({}, this.firstTemp)
|
||||
api.add(tempData).then((resp) => {
|
||||
...
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '提交成功',
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
该组件有以下几个参数:
|
||||
|
||||
form-fields:表单属性定义。为一个`ColumnDefine`的数组。典型的值如下:
|
||||
|
||||
```javascript
|
||||
this.firstHeaderList = [
|
||||
new ColumnDefine('id', 'id', false, false, 'text', '', 'string', 'varchar', ''),
|
||||
new ColumnDefine('tableName', '表名', true, true, 'text', '', 'string', 'varchar', ''),
|
||||
new ColumnDefine('parentTableId', '父表', true, true, 'selectDynamic', '/BuilderTables/AllMain', 'string', 'varchar', ''),
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: vue3代码生成器
|
||||
title: 单表代码生成
|
||||
createTime: 2025/04/23 23:43:26
|
||||
permalink: /pro/devnew/
|
||||
---
|
||||
@@ -49,7 +49,7 @@ getList() {
|
||||
|
||||
#### 固定头部
|
||||
|
||||
如果不是上面的情况,则生成的前端列为固定的头部。如下:
|
||||
代码生成的时候,按配置生成前端表头,一旦生成完毕,后台就无法通过授权等方式来控制显示的文字等。如下:
|
||||
|
||||
```html
|
||||
|
||||
@@ -68,6 +68,8 @@ initCfg() {
|
||||
|
||||
```
|
||||
|
||||
只能通过修改前端代码或使用[列设置](/pro/columnsetting/)组件来修改。
|
||||
|
||||
## 准备工作
|
||||
|
||||
* 数据库OpenAuthPro中添加仓储表【Stock】,本文以该数据表为例。参考表结构如下:
|
||||
@@ -110,27 +112,10 @@ create table stock
|
||||
|
||||
这时,编辑字段具体的属性。如是否【可显示】【可编辑】等。
|
||||
|
||||
|
||||
## 子表添加
|
||||
|
||||
::: warning 注意
|
||||
如果只生成单表结构,可以跳过本节
|
||||
:::
|
||||
|
||||
如果需要添加主/从(父/子)结构,需要先添加子表信息。代码生成界面,点击`添加`按钮,输入想添加的模块信息。
|
||||
|
||||

|
||||
|
||||
::: warning 注意
|
||||
子表的`动态头部`属性务必与父表保持一致,否则会造成生成的代码不能正常运行
|
||||
:::
|
||||
|
||||
## 生成模块
|
||||
|
||||
选中刚刚添加的`Stock`表,依次点击【生成实体】【生成业务代码】【生成vue页面】;
|
||||
|
||||
如果存在子表,也进行相同的操作。即选中刚刚添加的`StockDetail`表,依次点击【生成实体】【生成业务代码】【生成vue页面】;
|
||||
|
||||
成功后生成的后端.Net代码位置如下:
|
||||
|
||||
OpenAuth.Repository\Domain\Stock.cs
|
||||
@@ -139,11 +124,13 @@ OpenAuth.App\StockApp\Request\AddOrUpdateStockReq.cs
|
||||
OpenAuth.App\StockApp\Request\QueryStockListReq.cs
|
||||
OpenAuth.WebApi\Controllers\StocksController.cs
|
||||
|
||||
并且会在OpenAuth.Repository\OpenAuthDBContext.cs中自动添加:
|
||||
|
||||
::: warning 注意
|
||||
以前代码生成器生成基于EF的代码时,会在OpenAuth.Repository\OpenAuthDBContext.cs中自动添加:
|
||||
```
|
||||
public virtual DbSet<Stock> Stocks { get; set; }
|
||||
```
|
||||
现在基于SqlSugar的代码生成器,已不需要。如果有其他开发方面需要,可自行手动添加。
|
||||
:::
|
||||
|
||||
|
||||
前端Vue代码:
|
||||
@@ -151,9 +138,23 @@ src\api\stocks.js
|
||||
src\views\stocks\index.vue
|
||||
|
||||
::: warning 注意
|
||||
完成以上步骤后,请重启OpenAuth.WebApi,用来重新加载刚刚生成的后台代码
|
||||
1. 完成以上步骤后,请重启OpenAuth.WebApi,用来重新加载刚刚生成的后台代码
|
||||
|
||||
子表不需要添加模块
|
||||
2. 代码生成器生成的应用层XxxxApp.cs里面的编辑功能,只修改部分属性,且按帕斯卡命名。需要根据业务需要调整要编辑的字段。如:
|
||||
```csharp
|
||||
public void Update(AddOrUpdateChildtableReq obj)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
Repository.Update(u => new Childtable
|
||||
{
|
||||
//todo: 根据业务需要调整字段
|
||||
Updatetime = DateTime.Now,
|
||||
Updateuserid = user.Id,
|
||||
Updateusername = user.Name
|
||||
},u => u.Id == obj.Id);
|
||||
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
## 配置模块地址
|
||||
@@ -166,13 +167,21 @@ src\views\stocks\index.vue
|
||||
|
||||

|
||||
|
||||
尝试增加一条记录,看看模块是否能正常运行。
|
||||
|
||||
## 代码生成功能字段详解
|
||||
|
||||
* 模块名称:表示这个生成的模块名称,根据自己需要填写。
|
||||
* 模块名称:**必填** 表示这个生成的模块名称,根据自己需要填写。
|
||||
|
||||
* 模块编码:**必填** 表示生成的模块业务类名称,比如`XxxxxApp`。
|
||||
|
||||
* 命名空间:**必填** 表示生成的实体类的命名空间。默认`OpenAuth.Repository.Domain`。
|
||||
|
||||
* 表名:**必填** 表示数据库中对应的表名。
|
||||
|
||||
* 父表:表示主/从(父/子)表的父表。如果该项不选,则生成的是单表结构的代码,如果选择了父表,则生成主/从(父/子)表的代码。
|
||||
* 实体类名称:**必填** 表示生成的实体类名称。
|
||||
|
||||
* 父表:表示主/从表的父表。如果选择了父表,则当前表为子表,与对应的父表自动形成主/从关系。
|
||||
|
||||
* 外键:表示与父表关联的外键。如果选择了父表,则该项必填。
|
||||
|
||||
@@ -180,13 +189,125 @@ src\views\stocks\index.vue
|
||||
|
||||
* 动态头部:如果选中,表示前端渲染列表(或表单)时,列表的列定义是从后端返回。通常用于控制前端字段显示权限。
|
||||
|
||||
* 实体类名称:**必填** 表示生成的实体类名称。
|
||||
* Vue目录: 生成vue代码时,代码存放的目录,一般是vue项目的根目录。比如`D:\openauthvue3`。
|
||||
|
||||
* 命名空间:**必填** 表示生成的实体类的命名空间。默认`OpenAuth.Repository.Domain`。
|
||||
|
||||
* 模块编码:**必填** 表示生成的模块业务类名称,比如`XxxxxApp`。
|
||||
## 生成逻辑详解
|
||||
|
||||
* Vue目录: 生成vue代码时,代码存放的目录。比如`D:\openauthvue3`。
|
||||
::: warning 注意
|
||||
如果不想了解我们代码生成器生成代码的逻辑,可以跳过以下内容。
|
||||
:::
|
||||
|
||||
如果你觉得框架自带的代码生成器没达到你想要的效果,可以自行修改。以下是代码生成器逻辑概述:
|
||||
|
||||
当您在代码生成界面选中一个或多个表,并依次点击【生成实体】、【生成业务代码】、【生成vue页面】(及Vue API)时,系统会执行一系列后端操作来创建所需的代码文件。以下是这些操作的详细解析:
|
||||
|
||||
### 1. 生成实体
|
||||
|
||||
点击【生成实体】后,系统将为选中的数据库表创建对应的C#实体类。
|
||||
|
||||
* **核心方法**: `BuilderTableApp.CreateEntity(CreateEntityReq req)`
|
||||
* **主要流程**:
|
||||
1. **数据准备**: 根据请求中的ID加载表定义信息 (`BuilderTable`) 和所有字段定义信息 (`BuilderTableColumn`)。
|
||||
2. **重复性检查**: 调用 `CheckExistsModule` 方法,通过反射检查项目中是否已存在同名或已映射到同名数据库表的实体类,防止重复创建。
|
||||
3. **模型创建 (`CreateEntityModel`)**:
|
||||
* **模板**: 固定使用 `Template\BuildEntity.html`。
|
||||
* **基类选择**:
|
||||
* 根据主键的数据库类型 (`ColumnType`) 和是否自增 (`IsIncrement`),实体类会继承自:
|
||||
* `IntAutoGenEntity`: 若主键为数字类型 (如 `decimal`, `numeric`) 且自增。
|
||||
* `LongEntity`: 若主键为数字类型 (如 `decimal`, `numeric`) 但不自增 (通常用于雪花ID)。
|
||||
* `StringEntity`: 若主键为字符串等其他类型。
|
||||
* **属性生成**:
|
||||
* 遍历表中的每个字段(主键除外)。
|
||||
* 为字段添加 `/// <summary>` XML注释(源自字段备注)和 `[Description("字段备注")]` 特性。
|
||||
* 根据字段是否允许为空 (`IsRequired`) 和其数据类型 (`EntityType`),正确生成属性类型(例如,值类型若允许为空,则变为可空类型,如 `int?`)。
|
||||
* 生成 `public {字段类型} {字段名} { get; set; }`。
|
||||
* **构造函数初始化**:
|
||||
* 在实体类的构造函数中,为每个非主键属性赋予初始值 (例如 `string` 类型初始化为 `""`,`DateTime` 初始化为 `DateTime.Now`,`bool` 初始化为 `false` 等)。
|
||||
* **表注解**:
|
||||
* 为实体类添加 `/// <summary>` XML注释(源自表备注)。
|
||||
* 添加 `[Table("实际数据库表名")]` 特性,将实体类映射到数据库表。
|
||||
* **文件输出**: 生成的实体类代码最终会写入到 `OpenAuth.Repository\Domain\{实体类名}.cs` 文件。
|
||||
|
||||
### 2. 生成业务代码
|
||||
|
||||
点击【生成业务代码】后,系统会创建应用服务层(App)、相关的请求对象(Request DTOs)以及WebAPI控制器(Controller)。
|
||||
|
||||
* **核心方法**: `BuilderTableApp.CreateBusiness(CreateBusiReq req)`
|
||||
* **主要流程**: 此方法会依次调用以下三个内部方法:
|
||||
|
||||
* **A. `GenerateApp` (生成应用服务类)**
|
||||
1. **路径与检查**: 定位 `.App` 项目路径,并检查模块代码 (`ModuleCode`) 是否已存在。
|
||||
2. **模板选择**:
|
||||
* **主从表判断**: 根据当前表是否有子表 (`ParentTableId`),区分为单表或主从表生成逻辑。
|
||||
* **动态/固定头部**: 根据表配置的 `IsDynamicHeader` 属性,选择对应的模板。
|
||||
* 单表动态头部: `Template\SingleTable\BuildAppWithDynamicHeader.html`
|
||||
* 单表固定头部: `Template\SingleTable\BuildApp.html`
|
||||
* 主从表动态头部: `Template\MultiTable\BuildAppWithDynamicHeader.html`
|
||||
* 主从表固定头部: `Template\MultiTable\BuildApp.html`
|
||||
3. **基类选择**:
|
||||
* 应用服务类的基类根据主键类型决定:
|
||||
* `BaseIntAutoGenApp`: 对应 `IntAutoGenEntity`。
|
||||
* `BaseLongApp`: 对应 `LongEntity`。
|
||||
* `BaseStringApp`: 对应 `StringEntity`。
|
||||
4. **代码生成**:
|
||||
* 替换模板中的占位符,如 `{ClassName}` (实体类名), `{ModuleCode}` (模块代码), `{ModuleName}` (模块名), `{StartName}` (通常为项目主命名空间前缀如 `OpenAuth`)。
|
||||
* **外键处理**: 如果当前生成的是子表的App服务,会在查询列表中自动加入基于其外键 (`ForeignKey`) 的过滤条件。
|
||||
* **主从表逻辑**: 如果是主表且有关联子表,模板中会包含对子表操作的逻辑,并替换子表相关占位符如 `{SubClassName}`, `{SubForeignKey}`。
|
||||
5. **文件输出**: 生成的应用服务类写入到 `OpenAuth.App\{ModuleCode}\{ModuleCode}.cs`。
|
||||
|
||||
* **B. `GenerateAppReq` (生成请求DTO)**
|
||||
1. **路径**: 定位 `.App` 项目路径。
|
||||
2. **查询请求对象 (`Query{ClassName}ListReq.cs`)**:
|
||||
* 模板: `Template\BuildQueryReq.html`。
|
||||
* 内容: 主要包含分页、排序、关键词查询等基础参数。如果当前表是子表,还会自动添加其外键字段作为查询参数。
|
||||
* 文件输出: `OpenAuth.App\{ModuleCode}\Request\Query{ClassName}ListReq.cs`。
|
||||
3. **添加/更新请求对象 (`AddOrUpdate{ClassName}Req.cs`)**:
|
||||
* 模板: `Template\BuildUpdateReq.html`。
|
||||
* 内容:
|
||||
* 包含表中所有可编辑字段对应的属性,每个属性都有XML注释。
|
||||
* 如果当前是主表且有关联子表,会自动添加一个子表请求对象列表属性,如 `public List<AddOrUpdate{SubClassName}Req> {SubClassName}Reqs { get; set; }`,用于一次性提交主从数据。
|
||||
* 文件输出: `OpenAuth.App\{ModuleCode}\Request\AddOrUpdate{ClassName}Req.cs`。
|
||||
|
||||
* **C. `GenerateWebApi` (生成控制器)**
|
||||
1. **路径与检查**: 定位 `.WebApi` 项目路径,控制器名称通常为 `{ClassName}sController` (如 `StocksController`),并检查是否已存在。
|
||||
2. **模板**: `Template\BuildControllerApi.html`。
|
||||
3. **主键类型处理**: 控制器中的 `Get(id)`、`Update(id, req)` 等接口的 `id` 参数类型,会根据实体主键的实际类型(`int`, `long`/`decimal`, `string`)进行适配。
|
||||
4. **代码生成**: 替换模板占位符,注入对应的App服务。
|
||||
5. **文件输出**: 生成的控制器写入到 `OpenAuth.WebApi\Controllers\{ClassName}sController.cs`。
|
||||
|
||||
### 3. 生成Vue页面与API脚本
|
||||
|
||||
点击【生成vue页面】后,系统不仅会为选中的主表创建Vue视图文件,还会为其(以及其子表,如果存在)生成前端调用API的JavaScript文件。
|
||||
|
||||
* **核心方法 (Vue视图)**: `BuilderTableApp.CreateVue(CreateVueReq req)`
|
||||
* **核心方法 (Vue API脚本)**: `BuilderTableApp.CreateVueApi(CreateVueReq req)`
|
||||
|
||||
* **A. `CreateVue` (生成 `.vue` 文件)**:
|
||||
1. **路径校验**: 确保提供了Vue项目的根目录 (`req.VueProjRootPath`)。
|
||||
2. **子表跳过**: 如果当前选中的表是子表(即 `ParentTableId` 不为空),则不生成独立的Vue页面,因为子表通常在主表的页面中以内嵌表格形式管理。
|
||||
3. **模板选择**:
|
||||
* **主从表判断**: 区分单表和主从表。
|
||||
* **Vue版本与动态头部**: 根据请求中的Vue版本 (`req.Version`,如 'vue3') 和表的 `IsDynamicHeader` 配置,选择对应的模板:
|
||||
* Vue3单表动态头: `Template\SingleTable\BuildVue3WithDynamicHeader.html`
|
||||
* Vue3单表固定头: `Template\SingleTable\BuildVue3.html`
|
||||
* (Vue2的模板类似,如 `Template\SingleTable\BuildVue.html`)
|
||||
* 主从表模板类似 (如 `Template\MultiTable\BuildVue3.html`)
|
||||
4. **表头生成 (`BuilderHeader` 辅助方法)**:
|
||||
* 遍历表的字段配置 (`BuilderTableColumn`)。
|
||||
* 为每个需要在列表或表单中显示的字段,生成 `new ColumnDefine(...)` 的JavaScript代码。`ColumnDefine` 对象包含了字段的驼峰命名、显示名(备注)、是否可编辑、是否在列表显示、编辑控件类型 (`EditType`)、数据源 (`DataSource`,用于下拉框等)、原始实体类型、数据库列类型等信息。
|
||||
5. **代码生成与占位符替换**:
|
||||
* **单表**: 替换 `{ClassName}` (主表类名), `{TableName}` (主表类名驼峰式), `{HeaderList}` (主表头定义)。
|
||||
* **主从表**: 替换 `{FirstTableName}` (主表名驼峰), `{SecondTableName}` (子表名驼峰), `{FirstHeaderList}` (主表头), `{SecondHeaderList}` (子表头), `{ParentTableId}` (子表外键驼峰式,用于关联主从数据)。
|
||||
6. **文件输出**: 生成的Vue文件写入到 `{Vue项目根路径}\src\views\{主表类名小写}s\index.vue`。
|
||||
|
||||
* **B. `CreateVueApi` (生成 `apiname.js` 文件)**:
|
||||
1. **适用对象**: 无论是主表还是子表,都会为其生成API脚本。
|
||||
2. **模板**: `Template\BuildVueApi.html`。
|
||||
3. **代码生成**: 主要替换 `{TableName}` 占位符为表的类名驼峰式。模板中预设了对后端标准CRUD接口的调用函数 (如 `getList`, `add`, `update`, `del` 等)。
|
||||
4. **文件输出**: 生成的API脚本写入到 `{Vue项目根路径}\src\api\{表类名驼峰式}s.js`。
|
||||
|
||||
---
|
||||
|
||||
## 其他
|
||||
|
||||
|
||||
140
newdocs/docs/notes/pro/devnewmulti.md
Normal file
140
newdocs/docs/notes/pro/devnewmulti.md
Normal file
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: 多表代码生成
|
||||
createTime: 2025/04/23 23:43:26
|
||||
permalink: /pro/devnewmulti/
|
||||
---
|
||||
|
||||
本章节视频讲解请参考:[OpenAuth.Net视频合集--多表代码生成](https://www.bilibili.com/video/BV1r4J3zAEsx/)
|
||||
|
||||
上节介绍了[单表代码生成](/pro/devnew/),本节介绍多表代码生成。
|
||||
|
||||
## 准备工作
|
||||
|
||||
本文以mysql数据库为例,在openauthpro数据库中添加父表parenttable、子表childtable,参考表结构如下:
|
||||
|
||||
```SQL
|
||||
CREATE TABLE `parenttable` (
|
||||
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '父表ID',
|
||||
`externalno` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '自定义编号',
|
||||
`status` int(11) NOT NULL COMMENT '状态',
|
||||
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
|
||||
|
||||
`createtime` datetime NOT NULL COMMENT '创建时间',
|
||||
`createuserid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '创建人ID',
|
||||
`createusername` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '创建人',
|
||||
`updatetime` datetime NULL DEFAULT NULL COMMENT '最后更新时间',
|
||||
`updateuserid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后更新人ID',
|
||||
`updateusername` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后更新人',
|
||||
`orgid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '所属部门',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '父表' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
CREATE TABLE `childtable` (
|
||||
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '子表ID',
|
||||
`parentid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '父表ID',
|
||||
`remark` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '备注',
|
||||
`pricenotax` decimal(18, 6) NULL DEFAULT NULL COMMENT '单价',
|
||||
`asnstatus` int(11) NOT NULL COMMENT '细单状态',
|
||||
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品名称',
|
||||
`goodssku` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品SKU',
|
||||
`goodsbatch` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品批号',
|
||||
`ordernum` decimal(18, 2) NOT NULL COMMENT '数量',
|
||||
`proddate` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生产日期',
|
||||
`expiredate` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '失效日期',
|
||||
|
||||
`createtime` datetime NOT NULL COMMENT '创建时间',
|
||||
`createuserid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '创建人ID',
|
||||
`createusername` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '创建人',
|
||||
`updatetime` datetime NULL DEFAULT NULL COMMENT '最后更新时间',
|
||||
`updateuserid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后更新人ID',
|
||||
`updateusername` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后更新人',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '子表' ROW_FORMAT = DYNAMIC;
|
||||
```
|
||||
|
||||
* 使用Visual Studio 以开发模式启动后端。参考:[编译运行webapi](http://doc.openauth.net.cn/core/start/#%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8Cwebapi)
|
||||
|
||||
* 启动企业版前端。参考:[启动前端](http://doc.openauth.net.cn/pro/#%E5%90%AF%E5%8A%A8%E5%89%8D%E7%AB%AF)
|
||||
|
||||
后续操作全部在系统中完成。
|
||||
|
||||
## 添加父表
|
||||
|
||||
代码生成界面,点击`添加`按钮,输入想添加的模块信息。相关字段说明可参考[代码生成功能字段详解](/pro/devnew/#代码生成功能字段详解)
|
||||

|
||||
|
||||
录入基础信息,点击右下角`确认`保存后,系统会添加一条记录,并自动加载对应的字段信息。
|
||||
|
||||

|
||||
|
||||
这时,编辑字段具体的属性。如是否【可显示】【可编辑】等。
|
||||
|
||||
|
||||
## 添加子表
|
||||
|
||||
|
||||
同添加父表一样,点击`添加`按钮,输入子模块信息。
|
||||
|
||||

|
||||
|
||||
::: warning 注意
|
||||
* 子表的`父表`为必填字段,选择刚刚添加的父表。
|
||||
* 子表的`外键`字段为必填字段,选择子表设计的类似`父表ID`字段,从而可以与父表关联。
|
||||
* 子表的`动态头部`属性务必与父表保持一致,否则会造成生成的代码不能正常运行
|
||||
:::
|
||||
|
||||
## 生成模块
|
||||
|
||||
选中刚刚添加的父表,依次点击【生成实体】【生成业务代码】【生成vue页面】;
|
||||
子表虽然不需要生成vue页面,但需要生成前端js api;
|
||||
|
||||
成功后生成的后端.Net代码位置如下:
|
||||
|
||||
OpenAuth.Repository\Domain\Parentable.cs
|
||||
OpenAuth.App\ParentableApp\ParentableApp.cs
|
||||
OpenAuth.App\ParentableApp\Request\AddOrUpdateParentableReq.cs
|
||||
OpenAuth.App\ParentableApp\Request\QueryParentableListReq.cs
|
||||
OpenAuth.WebApi\Controllers\ParentablesController.cs
|
||||
|
||||
OpenAuth.Repository\Domain\Childtable.cs
|
||||
OpenAuth.App\ChildtableApp\ChildtableApp.cs
|
||||
OpenAuth.App\ChildtableApp\Request\AddOrUpdateChildtableReq.cs
|
||||
OpenAuth.App\ChildtableApp\Request\QueryChildtableListReq.cs
|
||||
OpenAuth.WebApi\Controllers\ChildtablesController.cs
|
||||
|
||||
前端Vue代码:
|
||||
src\api\parentables.js
|
||||
src\api\childtables.js
|
||||
src\views\parentables\index.vue
|
||||
|
||||
::: warning 注意
|
||||
1. 完成以上步骤后,请重启OpenAuth.WebApi,用来重新加载刚刚生成的后台代码
|
||||
|
||||
2. 代码生成器生成的应用层XxxxApp.cs里面的编辑功能,只修改部分属性,且按帕斯卡命名。需要根据业务需要调整要编辑的字段。如:
|
||||
```csharp
|
||||
public void Update(AddOrUpdateChildtableReq obj)
|
||||
{
|
||||
var user = _auth.GetCurrentUser().User;
|
||||
Repository.Update(u => new Childtable
|
||||
{
|
||||
//todo: 根据业务需要调整字段
|
||||
Updatetime = DateTime.Now,
|
||||
Updateuserid = user.Id,
|
||||
Updateusername = user.Name
|
||||
},u => u.Id == obj.Id);
|
||||
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
## 配置模块地址
|
||||
|
||||
经过以上步骤,重启系统后,使用System账号重新登录,在【模块管理】中,添加`测试模块`模块,如下图:
|
||||
|
||||

|
||||
|
||||
这样就可以访问刚刚新加的`测试模块`功能,到此就完成了添加一个新模块功能:
|
||||
|
||||

|
||||
|
||||
尝试增加一条记录,看看模块是否能正常运行。
|
||||
@@ -4,59 +4,86 @@ createTime: 2025/04/23 23:43:26
|
||||
permalink: /pro/printerplan/
|
||||
---
|
||||
|
||||
OpenAuth.Pro提供灵活无代码(可能需要写SQL)的打印解决方案,无需开发人员参与即可配置自己需要的打印。
|
||||
OpenAuth.Pro提供强大且灵活的低代码打印方案,企业用户仅需进行简单配置(可能需要编写SQL查询)即可实现个性化打印需求,无需开发人员参与即可快速部署各类打印模板。
|
||||
|
||||
打印模板设计界面如下:
|
||||
## 系统界面展示
|
||||
|
||||
### 打印模板设计界面
|
||||
|
||||
专业的设计工作台,支持拖拽式操作与精确配置:
|
||||
|
||||

|
||||
|
||||
挂载到业务系统中效果:
|
||||
### 业务系统集成效果
|
||||
|
||||
无缝集成到各类业务功能模块中:
|
||||
|
||||

|
||||
|
||||
|
||||
## 使用指南
|
||||
## 详细使用指南
|
||||
|
||||
### 设计打印模版
|
||||
### 创建与设计打印模版
|
||||
|
||||
在打印中心-打印模版功能新增打印模版。设计界面说明如下:
|
||||
在【打印中心】-【打印模版】功能中新增打印模版。设计界面布局如下:
|
||||
|
||||

|
||||
|
||||
左边为设计工具栏,中间为画布,右边为属性框。可以根据自己需要调整打印界面。
|
||||
界面分为三大区域:
|
||||
- 左侧:设计工具栏,提供各类控件与格式设置工具
|
||||
- 中间:设计画布,实时预览打印效果
|
||||
- 右侧:属性配置区,精确调整各元素参数与样式
|
||||
|
||||
### 设置数据源
|
||||
用户可根据实际需求,通过拖拽与配置快速构建专业打印模板。
|
||||
|
||||
打印模版的核心是设计数据源。用户通过设计数据源,实现无开发打印。如下:
|
||||
### 配置数据源
|
||||
|
||||
数据源是打印模版的核心,通过合理设计数据源,实现真正的低代码打印解决方案:
|
||||
|
||||

|
||||
|
||||
1. 数据源:用于查询打印打印数据。
|
||||
|
||||
2. Id:由数据源中的SQL自动生成的参数文本框。
|
||||
|
||||
3. 分组字段:如果配置了`分组字段`,则默认对应列表式打印模版。否则,可以看做卡片式。
|
||||
关键配置项说明:
|
||||
|
||||
* 【卡片式】用于打印一页只有一条数据的情况。比如快递的标签、用户名片等。
|
||||
1. **数据源**:用于查询打印数据的SQL语句,支持复杂查询与条件筛选
|
||||
|
||||
2. **Id参数**:系统根据数据源SQL自动生成的参数文本框,用于动态传递查询条件
|
||||
|
||||
3. **分组字段**:决定打印模版的类型与数据结构处理方式
|
||||
- 配置分组字段:生成列表式打印模版(一对多数据结构)
|
||||
- 不配置分组字段:生成卡片式打印模版(单条数据记录)
|
||||
|
||||
### 打印模式类型说明
|
||||
|
||||
系统支持两种主要打印模式,适应不同业务场景:
|
||||
|
||||
* **卡片式打印**:适用于单条记录的打印场景,如快递标签、会员卡、用户名片等
|
||||

|
||||
* 【列表式】用于打印一页有多条数据的情况,通常是主从结构的表,同时打印主表及从表详情。如客户的发货清单。
|
||||

|
||||
|
||||
::: warning 注意
|
||||
* **列表式打印**:适用于主从结构数据的打印场景,一页包含多条关联数据,如销售订单、发货清单等
|
||||

|
||||
|
||||
两种模版类型没有严格的区分。在后端返回相同的数据情况下,如果配置了`分组字段`,后端返回的数据会被转化成主从结构。上图即为官网【入库随货同行单(列表式)【带参数】】按OrderId进行分组后的数据。
|
||||
::: warning 特别说明
|
||||
|
||||
两种模版类型在底层处理上存在灵活性。当配置了`分组字段`时,后端将自动将数据按该字段进行分组,转化为主从结构。例如,上图展示的【入库随货同行单(列表式)【带参数】】正是通过按OrderId分组后生成的数据结构。
|
||||
|
||||
:::
|
||||
|
||||
### 功能模块挂载打印方案
|
||||
### 打印方案与功能模块关联
|
||||
|
||||
制作完打印方案后,在【模块管理】功能里面,把刚刚打印方案与功能关联。
|
||||
完成打印方案设计后,需在【模块管理】功能中将打印方案与业务功能关联,实现功能集成:
|
||||
|
||||

|
||||
|
||||
|
||||
### 使用打印
|
||||
### 实际打印操作
|
||||
|
||||
最后就可以在功能里面使用打印了。
|
||||
完成上述配置后,用户可在相应业务功能中直接调用打印方案,实现一键打印:
|
||||
|
||||

|
||||

|
||||
|
||||
## 最佳实践建议
|
||||
|
||||
1. 设计数据源时,建议先测试SQL查询结果,确保数据结构符合预期
|
||||
2. 打印模板设计时注意适配不同纸张规格,预留适当边距
|
||||
3. 对于复杂报表,可考虑使用分组字段实现数据的合理组织
|
||||
4. 利用系统提供的预览功能,及时调整布局与样式
|
||||
@@ -4,9 +4,7 @@ createTime: 2025/05/07 19:10:57
|
||||
permalink: /pro/querybuilder/
|
||||
---
|
||||
|
||||
## 组件简介
|
||||
|
||||
自定义查询组件是一个可复用的高级查询条件构建器组件,支持通过配置 columns 自动生成 SQL 查询条件。用于配合框架el-table及[通用表格组件](./table.md)的复杂筛选的场景,支持条件的增删、类型自动识别、SQL 片段生成与复制、条件本地缓存等功能。
|
||||
自定义查询组件`QueryBuilder`是一个可复用的高级查询条件构建器组件,支持通过配置 columns 自动生成 SQL 查询条件。用于配合框架el-table及[通用表格组件](./table.md)的复杂筛选的场景,支持条件的增删、类型自动识别、SQL 片段生成与复制、条件本地缓存等功能。
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ createTime: 2025/05/07 19:10:57
|
||||
permalink: /pro/table/
|
||||
---
|
||||
|
||||
## 组件简介
|
||||
通用表格组件`AuthTable` 是OpenAuth.Net框架的核心组件之一,支持自定义列模板、单选/多选、可编辑、导出 Excel 等功能。
|
||||
|
||||
`AuthTable` 是一个通用表格组件,支持自定义列模板、单选/多选、可编辑、导出 Excel 等功能。
|
||||

|
||||
|
||||
## 使用示例
|
||||
|
||||
|
||||
BIN
snapshot/maintable.jpg
Normal file
BIN
snapshot/maintable.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
Reference in New Issue
Block a user