feat: initialize Evergreen Health backend

This commit is contained in:
fuenmao
2026-09-21 14:08:22 +08:00
commit 8553df5dd2
493 changed files with 75649 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

@@ -0,0 +1,30 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace @(Model.NameSpace);
/// <summary>
/// @(Model.BusName)输出参数
/// </summary>
public class @(Model.ClassName)Dto
{
@foreach (var column in Model.TableField){
if(column.EffectType == "ForeignKey"){
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
@:public string @(column.PropertyName)FkColumn { get; set; }
@:
}
}
@foreach (var column in Model.TableField){
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
@:public @column.NetType @column.PropertyName { get; set; }
@:
}
}
@@ -0,0 +1,46 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
@if(Model.BaseClassName!=""){
@:using Admin.NET.Core;
@:using SqlSugar;
}
namespace @(Model.NameSpace);
/// <summary>
/// @(Model.Description)
/// </summary>
[Tenant("@(Model.ConfigId)")]
[SugarTable("@(Model.TableName)", "@(Model.Description)")]
public partial class @(Model.EntityName) @Model.BaseClassName
{
@foreach (var column in Model.TableField) {
var propSuffix = "";
if (column.IsPrimarykey && (Model.BaseClassName == "" || Model.BaseClassName != "" && column.DbColumnName.ToLower() != "id")) {
propSuffix = $", IsPrimaryKey = true, IsIdentity = {column.IsIdentity.ToString().ToLower()}";
}
if (column.DataType.TrimEnd('?') == "string") {
propSuffix += $", Length = {column.Length}";
} else if (column.DataType.TrimEnd('?') == "decimal") {
propSuffix += $", Length = {column.Length}, DecimalDigits={column.DecimalDigits}";
}
if(!string.IsNullOrWhiteSpace(column.DefaultValue)){
propSuffix +=$", DefaultValue = \"{column.DefaultValue}\"";
}
@:/// <summary>
@:/// @column.ColumnDescription
@:/// </summary>
if(!column.IsNullable){
@:[Required]
}
@:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription"@propSuffix)]
@:public virtual @column.DataType @column.PropertyName { get; set; }
@:
}
}
@@ -0,0 +1,200 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core;
using System.ComponentModel.DataAnnotations;
using Magicodes.ExporterAndImporter.Core;
using Magicodes.ExporterAndImporter.Excel;
namespace @(Model.NameSpace);
/// <summary>
/// @(Model.BusName)基础输入参数
/// </summary>
public class @(Model.ClassName)BaseInput
{
@foreach (var column in Model.PrimaryKeyFieldList.Concat(Model.AddUpdateFieldList)){
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
if (column.EffectType is "EnumSelector" or "DictSelector") {
@:[Dict(@(column.EffectType == "EnumSelector" ? $"nameof({column.DictTypeCode})" : $"\"{column.DictTypeCode}\""), AllowNullValue=true)]
}
if (column.WhetherRequired == "Y") {
@:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")]
}
@:public virtual @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
@:
}
}
/// <summary>
/// @(Model.BusName)分页查询输入参数
/// </summary>
public class Page@(Model.ClassName)Input : BasePageInput
{
@foreach (var column in Model.TableField.Where(u => u.WhetherQuery == "Y")){
if(column.NetType?.TrimEnd('?') == "DateTime" && column.QueryType == "~"){
@:/// <summary>
@:/// @(column.ColumnComment)范围
@:/// </summary>
@: public DateTime?[] @(column.PropertyName)Range { get; set; }
} else {
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
if (column.EffectType is "EnumSelector" or "DictSelector") {
@:[Dict(@(column.EffectType == "EnumSelector" ? $"nameof({column.DictTypeCode})" : $"\"{column.DictTypeCode}\""), AllowNullValue=true)]
}
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
}
@:
}
@if (Model.ImportFieldList.Count > 0){
var primaryKey = Model.PrimaryKeyFieldList.First();
@:/// <summary>
@:/// 选中主键列表
@:/// </summary>
@: public List<@(primaryKey.NetType)> SelectKeyList { get; set; }
}
}
/// <summary>
/// @(Model.BusName)增加输入参数
/// </summary>
public class Add@(Model.ClassName)Input
{
@foreach (var column in Model.AddUpdateFieldList){
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
if (column.EffectType is "EnumSelector" or "DictSelector") {
@:[Dict(@(column.EffectType == "EnumSelector" ? $"nameof({column.DictTypeCode})" : $"\"{column.DictTypeCode}\""), AllowNullValue=true)]
}
if (column.WhetherRequired == "Y") {
@:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")]
}
if (column.NetType.TrimEnd('?').EndsWith("string") && column.ColumnLength > 0){
@:[MaxLength(@column.ColumnLength, ErrorMessage = "@(column.ColumnComment)字符长度不能超过@(column.ColumnLength)")]
}
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
@:
}
}
/// <summary>
/// @(Model.BusName)删除输入参数
/// </summary>
public class Delete@(Model.ClassName)Input
{
@foreach (var column in Model.PrimaryKeyFieldList) {
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
@:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")]
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
@:
}
}
/// <summary>
/// @(Model.BusName)更新输入参数
/// </summary>
public class Update@(Model.ClassName)Input
{
@foreach (var column in Model.PrimaryKeyFieldList.Concat(Model.AddUpdateFieldList)){
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
if (column.EffectType is "EnumSelector" or "DictSelector") {
@:[Dict(@(column.EffectType == "EnumSelector" ? $"nameof({column.DictTypeCode})" : $"\"{column.DictTypeCode}\""), AllowNullValue=true)]
}
if (column.WhetherRequired == "Y" || column.ColumnKey == "True") {
@:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")]
}
if (column.NetType.TrimEnd('?').EndsWith("string") && column.ColumnLength > 0){
@:[MaxLength(@column.ColumnLength, ErrorMessage = "@(column.ColumnComment)字符长度不能超过@(column.ColumnLength)")]
}
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
@:
}
}
/// <summary>
/// @(Model.BusName)主键查询输入参数
/// </summary>
public class QueryById@(Model.ClassName)Input : Delete@(Model.ClassName)Input
{
}
@if (Model.DropdownFieldList.Count > 0) {
@:/// <summary>
@:/// 下拉数据输入参数
@:/// </summary>
@:public class DropdownData@(Model.ClassName)Input
@:{
@:/// <summary>
@:/// 是否用于分页查询
@:/// </summary>
@:public bool FromPage { get; set; }
@:}
@:
}
@if (Model.HasSetStatus) {
@:/// <summary>
@:/// 设置状态输入参数
@:/// </summary>
@:public class Set@(Model.ClassName)StatusInput : BaseStatusInput
@:{
@foreach (var column in Model.PrimaryKeyFieldList.Where(u => u.PropertyName != "Id")) {
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
@:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")]
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
@:
}
@:}
@:
}
@if (Model.ImportFieldList.Count > 0){
@:/// <summary>
@:/// @(Model.BusName)数据导入实体
@:/// </summary>
@:[ExcelImporter(SheetIndex = 1, IsOnlyErrorRows = true)]
@:public class Import@(Model.ClassName)Input : BaseImportInput
@:{
foreach (var column in Model.ImportFieldList){
var headerName = (column.WhetherRequired == "Y" ? "*" : "") + column.ColumnComment;
if(column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector" || column.EffectType == "DictSelector") {
@:/// <summary>
@:/// @column.ColumnComment 关联值
@:/// </summary>
@:[ImporterHeader(IsIgnore = true)]
@:[ExporterHeader(IsIgnore = true)]
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
@:
@:/// <summary>
@:/// @column.ColumnComment 文本
@:/// </summary>
if (column.EffectType == "DictSelector") {
@:[Dict(@($"\"{column.DictTypeCode}\""))]
}
@:[ImporterHeader(Name = "@(headerName)")]
@:[ExporterHeader("@(headerName)", Format = "@", Width = 25, IsBold = true)]
@:public string @column.ExtendedPropertyName { get; set; }
} else {
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
@:[ImporterHeader(Name = "@(headerName)")]
@:[ExporterHeader("@(headerName)", Format = "@", Width = 25, IsBold = true)]
@:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
}
@:
}
@:}
}
@@ -0,0 +1,71 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Magicodes.ExporterAndImporter.Core;
namespace @(Model.NameSpace);
/// <summary>
/// @(Model.BusName)输出参数
/// </summary>
public class @(Model.ClassName)Output
{
@foreach (var column in Model.TableField){
@:/// <summary>
@:/// @column.ColumnComment
@:/// </summary>
@:public @column.NetType @(column.PropertyName) { get; set; }
if(column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
@:
@:/// <summary>
@:/// @(column.ColumnComment) 描述
@:/// </summary>
@:public string @(column.ExtendedPropertyName) { get; set; }
}else if(column.EffectType == "Upload" || column.EffectType == "Upload_SingleFile"){
@:
@:/// <summary>
@:/// @(column.ColumnComment) 附件信息
@:/// </summary>
@:public SysFile @(column.ExtendedPropertyName) { get; set; }
}
@:
}
}
@if (Model.ApiTreeFieldList.Count > 0) {
foreach(var column in Model.ApiTreeFieldList) {
@:/// <summary>
@:/// @column.ColumnComment 树选择器输出参数
@:/// </summary>
@:public class Tree@(column.PropertyNameTrimEndId)Output : @(Model.ClassName)
@:{
@:/// <summary>
@:/// 节点值
@:/// </summary>
@:public @column.NetType Value { get; set; }
@:
@:/// <summary>
@:/// 节点文本
@:/// </summary>
@:public string Label { get; set; }
@:
@:/// <summary>
@:/// 子集数据
@:/// </summary>
@:public List<Tree@(column.PropertyNameTrimEndId)Output> Children { get; set; }
@:}
@:
}
}
@if (Model.ImportFieldList.Count > 0) {
@:
@:/// <summary>
@:/// @(Model.BusName)数据导入模板实体
@:/// </summary>
@:public class Export@(Model.ClassName)Output : Import@(Model.ClassName)Input
@:{
@: [ImporterHeader(IsIgnore = true)]
@: [ExporterHeader(IsIgnore = true)]
@: public override string Error { get; set; }
@:}
}
@@ -0,0 +1,29 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core;
using @Model.EntityNameSpace;
namespace @(Model.NameSpace);
/// <summary>
/// @(Model.Description) 表种子数据
/// </summary>
public class @(Model.SeedDataName) : ISqlSugarEntitySeedData<@(Model.EntityName)>
{
/// <summary>
/// 种子数据
/// </summary>
/// <returns></returns>
public IEnumerable<@(Model.EntityName)> HasData()
{
return new List<@(Model.EntityName)> {
@foreach (var record in Model.RecordList) {
@:new() { @record },
}
};
}
}
@@ -0,0 +1,421 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core.Service;
using Microsoft.AspNetCore.Http;
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using Mapster;
using SqlSugar;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using @(Model.NameSpace).Entity;
namespace @(Model.NameSpace);
/// <summary>
/// @(Model.BusName)服务 🧩
/// </summary>
[ApiDescriptionSettings(@(Model.ProjectLastName)Const.GroupName, Order = 100)]
public partial class @(Model.ClassName)Service : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<@(Model.ClassName)> _@(Model.LowerClassName)Rep;
@foreach(var kv in Model.InjectServiceMap) {
@:private readonly @(kv.Key) _@(kv.Value);
}
public @(Model.ClassName)Service(SqlSugarRepository<@(Model.ClassName)> @(Model.LowerClassName)Rep@(Model.InjectServiceArgs))
{
_@(Model.LowerClassName)Rep = @(Model.LowerClassName)Rep;
@foreach(var kv in Model.InjectServiceMap) {
@:_@(kv.Value) = @(kv.Value);
}
}
/// <summary>
/// 分页查询@(Model.BusName) 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询@(Model.BusName)")]
[ApiDescriptionSettings(Name = "Page"), HttpPost]
public async Task<SqlSugarPagedList<@(Model.ClassName)Output>> Page(Page@(Model.ClassName)Input input)
{
input.Keyword = input.Keyword?.Trim();
var query = _@(Model.LowerClassName)Rep.AsQueryable()
@{
string joinTableName = "u";
var queryFields = Model.TableField.Where(u => u.WhetherQuery == "Y");
// 关键字模糊查询
if (queryFields.Any(u => u.QueryType == "like")) {
@:.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => @string.Join(" || ", queryFields.Where(u => u.QueryType == "like").Select(col => $"u.{col.PropertyName}.Contains(input.Keyword)")))
}
// 单字段模糊查询
foreach(var column in queryFields.Where(u => u.QueryType == "like")) {
@:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)), u => u.@(column.PropertyName).Contains(input.@(column.PropertyName).Trim()))
}
// 字段组合查询
foreach(var column in queryFields.Where(u => u.QueryType != "like")) {
if (column.NetType.TrimEnd('?') == "string") {
@:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)), u => u.@(column.PropertyName) == input.@(column.PropertyName))
} else if (column.NetType.TrimEnd('?') == "int" || column.NetType.TrimEnd('?') == "long") {
@:.WhereIF(input.@(column.PropertyName) != null, u => u.@(column.PropertyName) @(column.QueryType) input.@(column.PropertyName))
} else if (column.NetType.TrimEnd('?').EndsWith("Enum")) {
@:.WhereIF(input.@(column.PropertyName).HasValue, u => u.@(column.PropertyName) == input.@(column.PropertyName))
} else if (column.NetType.TrimEnd('?') == "DateTime" && column.QueryType == "~") {
@:.WhereIF(input.@(column.PropertyName)Range?.Length == 2, u => u.@(column.PropertyName) >= input.@(column.PropertyName)Range[0] && u.@(column.PropertyName) <= input.@(column.PropertyName)Range[1])
} else if (column.NetType.TrimEnd('?').EndsWith("bool")) {
@:.WhereIF(input.@(column.PropertyName).HasValue, u => u.@(column.PropertyName) == input.@(column.PropertyName))
}
}
// 联表
if (Model.HasJoinTable) {
foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")){
joinTableName += ", " + column.LowerPropertyNameTrimEndId;
@:.LeftJoin<@column.FkEntityName>((@joinTableName) => u.@(column.PropertyName) == @(column.LowerPropertyNameTrimEndId).@(column.FkLinkColumnName))
}
// 查询列表
@:.Select((@joinTableName) => new @(Model.ClassName)Output
@:{
foreach (var column in Model.TableField) {
if(column.NetType.TrimEnd('?').EndsWith("Enum")) {
@:@(column.PropertyName) = (@(column.NetType))u.@(column.PropertyName),
}else{
@:@(column.PropertyName) = u.@(column.PropertyName),
}
if (column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
@:@(column.ExtendedPropertyName) = @column.GetDisplayColumn(column.LowerPropertyNameTrimEndId),
}
}
@:});
} else {
// 无联表
@:.Select<@(Model.ClassName)Output>();
}
}
return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
}
/// <summary>
/// 获取@(Model.BusName)详情 ℹ️
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("获取@(Model.BusName)详情")]
[ApiDescriptionSettings(Name = "Detail"), HttpGet]
public async Task<@(Model.ClassName)> Detail([FromQuery] QueryById@(Model.ClassName)Input input)
{
return await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}"));
}
/// <summary>
/// 增加@(Model.BusName) ➕
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("增加@(Model.BusName)")]
[ApiDescriptionSettings(Name = "Add"), HttpPost]
public async Task<long> Add(Add@(Model.ClassName)Input input)
{
var entity = input.Adapt<@(Model.ClassName)>();
@foreach (var config in Model.TableUniqueConfigList) {
@:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @(string.Join(" && ", @config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
}
return await _@(Model.LowerClassName)Rep.InsertAsync(entity) ? entity.Id : 0;
}
/// <summary>
/// 更新@(Model.BusName) ✏️
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("更新@(Model.BusName)")]
[ApiDescriptionSettings(Name = "Update"), HttpPost]
public async Task Update(Update@(Model.ClassName)Input input)
{
@{
var primaryKeyWhere = Model.PrimaryKeysFormat(" && ", "u.{0} != input.{0}");
foreach (var config in Model.TableUniqueConfigList) {
@:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @primaryKeyWhere && @config.Format(" && ", "u.{0} != null && u.{0} == input.{0}"))) throw Oops.Oh("@(config.Message)已存在");
}
}
var entity = input.Adapt<@(Model.ClassName)>();
await _@(Model.LowerClassName)Rep.AsUpdateable(entity)
@if (Model.IgnoreUpdateFieldList.Count > 0) {
@:.IgnoreColumns(u => new {
foreach (var column in Model.IgnoreUpdateFieldList) {
@:u.@(column.PropertyName),
}
@:})
}
.ExecuteCommandAsync();
}
/// <summary>
/// 删除@(Model.BusName) ❌
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("删除@(Model.BusName)")]
[ApiDescriptionSettings(Name = "Delete"), HttpPost]
public async Task Delete(Delete@(Model.ClassName)Input input)
{
var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
@{
@if(Model.TableField.Where(x => x.ColumnName == "IsDelete").Any())
{
@:await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity); //假删除
}
else{
@:await _@(Model.LowerClassName)Rep.DeleteAsync(entity); //真删除
}
}
}
/// <summary>
/// 批量删除@(Model.BusName) ❌
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("批量删除@(Model.BusName)")]
[ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<Delete@(Model.ClassName)Input> input)
{
var exp = Expressionable.Create<@(Model.ClassName)>();
foreach (var row in input) exp = exp.Or(it => @Model.PrimaryKeysFormat(" && ", "it.{0} == row.{0}"));
var list = await _@(Model.LowerClassName)Rep.AsQueryable().Where(exp.ToExpression()).ToListAsync();
@{
@if(Model.TableField.Where(x => x.ColumnName == "IsDelete").Any())
{
@:return await _@(Model.LowerClassName)Rep.FakeDeleteAsync(list); //假删除
}
else{
@:return await _@(Model.LowerClassName)Rep.Context.Deleteable(list).ExecuteCommandAsync(); //真删除--返回受影响的行数
}
}
}
@if (Model.HasSetStatus) {
@:
@:/// <summary>
@:/// 设置@(Model.BusName)状态 🚫
@:/// </summary>
@:/// <param name="input"></param>
@:/// <returns></returns>
@:[DisplayName("设置@(Model.BusName)状态")]
@:[ApiDescriptionSettings(Name = "SetStatus"), HttpPost]
@:public async Task Set@(Model.ClassName)Status(Set@(Model.ClassName)StatusInput input)
@:{
@:await _@(Model.LowerClassName)Rep.AsUpdateable().SetColumns(u => u.Status, input.Status).Where(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")).ExecuteCommandAsync();
@:}
}
@foreach (var column in Model.UploadFieldList) {
@:
@:/// <summary>
@:/// 上传@(column.ColumnComment) ⬆️
@:/// </summary>
@:/// <param name="file"></param>
@:/// <returns></returns>
@:[DisplayName("上传@(column.ColumnComment)")]
@:[ApiDescriptionSettings(Name = "Upload@(column.PropertyName)"), HttpPost]
@:public async Task<SysFile> Upload@(column.PropertyName)([Required] IFormFile file)
@:{
@:return await _sysFileService.UploadFile(new UploadFileInput { File = file },"upload/@(Model.ClassName)/@(column.PropertyName)");
@:}
}
@if (Model.DropdownFieldList.Count > 0) {
@:
@:/// <summary>
@:/// 获取下拉列表数据 🔖
@:/// </summary>
@:/// <returns></returns>
@:[DisplayName("获取下拉列表数据")]
@:[ApiDescriptionSettings(Name = "DropdownData"), HttpPost]
@:public async Task<Dictionary<string, dynamic>> DropdownData(DropdownData@(Model.ClassName)Input input)
@:{
foreach (var column in Model.DropdownFieldList) {
@:var @(column.LowerPropertyName)Data = await _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>()
if (column.EffectType != "ApiTreeSelector") {
@:.InnerJoinIF<@Model.ClassName>(input.FromPage, (u, r) => u.@(column.FkLinkColumnName) == r.@(column.PropertyName))
}
if (column.EffectType != "ApiTreeSelector") {
@:.Select(u => new {
@:Value = u.@(column.FkLinkColumnName),
@:Label = @column.GetDisplayColumn("u")
@:}).ToListAsync();
} else {
@:.Select(u => new Tree@(column.PropertyNameTrimEndId)Output {
@:Value = u.@(column.FkLinkColumnName),
@:Label = @column.GetDisplayColumn("u")
@:}, true).ToTreeAsync(u => u.Children, u => u.@(column.PidColumn), @(column.WhetherRequired == "Y" ? "0" : "null"));
}
}
@:return new Dictionary<string, dynamic>
@:{
foreach (var column in Model.DropdownFieldList) {
@:{ "@(column.LowerPropertyName)", @(column.LowerPropertyName)Data },
}
@:};
@:}
}
@if (Model.ImportFieldList.Count > 0) {
@:
@:/// <summary>
@:/// 导出@(Model.BusName)记录 🔖
@:/// </summary>
@:/// <param name="input"></param>
@:/// <returns></returns>
@:[DisplayName("导出@(Model.BusName)记录")]
@:[ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify]
@:public async Task<IActionResult> Export(Page@(Model.ClassName)Input input)
@:{
@:var list = (await Page(input)).Items?.Adapt<List<Export@(Model.ClassName)Output>>() ?? new();
@:if (input.SelectKeyList?.Count > 0) list = list.Where(x => input.SelectKeyList.Contains(x.@(Model.PrimaryKeyFieldList.First().PropertyName))).ToList();
var dictFields = Model.TableField.Where(x => x.WhetherImport == "Y" && x.EffectType == "DictSelector") ?? default;
foreach (var column in dictFields) {
@:var @(column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(column.DictTypeCode)" }).Result.ToDictionary(x => x.Value, x => x.Label);
}
if (dictFields.Count() > 0) {
@:list.ForEach(e => {
foreach (var column in dictFields) {
@:e.@(column.ExtendedPropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(e.@(column.PropertyName) ?? "", e.@(column.PropertyName));
}
@:});
}
@:return ExcelHelper.ExportTemplate(list, "@(Model.BusName)导出记录");
@:}
@:
@:/// <summary>
@:/// 下载@(Model.BusName)数据导入模板 ⬇️
@:/// </summary>
@:/// <returns></returns>
@:[DisplayName("下载@(Model.BusName)数据导入模板")]
@:[ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
@:public IActionResult DownloadTemplate()
@:{
var fieldsList = Model.ImportFieldList.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList();
if (fieldsList.Any()) {
@:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板", (_, info) =>
@:{
foreach (var column in fieldsList) {
var columnList = column.FkDisplayColumnList.Select(n => $"{{u.{n}}}").ToList();
@:if (nameof(Export@(Model.ClassName)Output.@column.ExtendedPropertyName) == info.Name) return _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Select(u => $"@(string.Join("-", columnList))").Distinct().ToList();
}
@:return null;
@:});
} else {
@:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板");
}
@:}
@:
@:private static readonly object _@(Model.LowerClassName)ImportLock = new object();
@:/// <summary>
@:/// 导入@(Model.BusName)记录 💾
@:/// </summary>
@:/// <returns></returns>
@:[DisplayName("导入@(Model.BusName)记录")]
@:[ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
@:public IActionResult ImportData([Required] IFormFile file)
@:{
@:lock (_@(Model.LowerClassName)ImportLock)
@:{
var dictTableField = Model.TableField.Where(x => x.WhetherImport == "Y" && x.EffectType == "DictSelector") ?? default;
foreach (var column in dictTableField){
@:var @(column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(column.DictTypeCode)" }).Result.ToDictionary(x => x.Label!, x => x.Value);
}
@:var stream = ExcelHelper.ImportData<Import@(Model.ClassName)Input, @(Model.ClassName)>(file, (list, markerErrorAction) =>
@:{
@:_sqlSugarClient.Utilities.PageEach(list, 2048, pageItems =>
@:{
foreach (var column in Model.ImportFieldList.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")) {
@:// 链接 @(column.ColumnComment)
@:var @(column.LowerPropertyName)LabelList = pageItems.Where(x => x.@column.ExtendedPropertyName != null).Select(x => x.@column.ExtendedPropertyName).Distinct().ToList();
@:if (@(column.LowerPropertyName)LabelList.Any()) {
var columnList = column.FkDisplayColumnList.Select(n => $"{{u.{n}}}").ToList();
@:var @(column.LowerPropertyName)LinkMap = _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Where(u => @(column.LowerPropertyName)LabelList.Contains($"@(string.Join("-", columnList))")).ToList().ToDictionary(u => $"@(string.Join("-", columnList))", u => u.@(column.FkLinkColumnName) as @(column.NetType.TrimEnd('?') == "long" ? "long?": column.NetType));
@:pageItems.ForEach(e => {
@:e.@(column.PropertyName) = @(column.LowerPropertyName)LinkMap.GetValueOrDefault(e.@column.ExtendedPropertyName ?? "");
@:if (e.@(column.PropertyName) == null) e.Error = "@(column.ColumnComment)链接失败";
@:});
@:}
}
if (dictTableField.Any()) {
@:
@:// 映射字典值
@:foreach(var item in pageItems) {
@:System.Text.StringBuilder sbError = new System.Text.StringBuilder();
foreach (var column in dictTableField) {
@:if (!string.IsNullOrWhiteSpace(item.@(column.ExtendedPropertyName))) {
@:item.@(column.PropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(item.@(column.ExtendedPropertyName));
@:if (item.@(column.PropertyName) == null) sbError.AppendLine("@(column.ColumnComment)字典映射失败");
@:}
}
@:item.Error = sbError.ToString();
@:}
}
@:
@:// 校验并过滤必填基本类型为null的字段
@:var rows = pageItems.Where(x => {
@:if (!string.IsNullOrWhiteSpace(x.Error)) return false;
foreach (var column in Model.ImportFieldList.Where(x => x.WhetherRequired == "Y" && Regex.IsMatch(x.NetType, "(int|long|double|float|bool|Enum[?]?)"))){
@:if (x.@(column.PropertyName) == null){
@:x.Error = "@(column.ColumnComment)不能为空";
@:return false;
@:}
}
@:return true;
@:}).Adapt<List<@(Model.ClassName)>>();
@{var updateFields = new List<string>();}
@:
@:var storageable = _@(Model.LowerClassName)Rep.Context.Storageable(rows)
foreach (var column in Model.ImportFieldList){
if (column.WhetherImport == "Y"){
updateFields.Add(column.PropertyName);
}
if (column.WhetherRequired == "Y"){
if(column.NetType.TrimEnd('?') == "string"){
@:.SplitError(it => string.IsNullOrWhiteSpace(it.Item.@(column.PropertyName)), "@(column.ColumnComment)不能为空")
} else if(column.NetType.EndsWith('?') == true){
@:.SplitError(it => it.Item.@(column.PropertyName) == null, "@(column.ColumnComment)不能为空")
}}
if (column.NetType?.TrimEnd('?') == "string" && column.ColumnLength > 0){
@:.SplitError(it => it.Item.@(column.PropertyName)?.Length > @(column.ColumnLength), "@(column.ColumnComment)长度不能超过@(column.ColumnLength)个字符")
}}
if(Model.TableUniqueConfigList.Count>0){
foreach (var config in Model.TableUniqueConfigList) {
@:.WhereColumns(it => new { @config.Format(", ", "it.{0}") })
}
@:.SplitInsert(it=> !it.Any())
@:.SplitUpdate(it=> it.Any())
}else{
@:.SplitInsert(_=> true) // 没有设置唯一键代表插入所有数据
}
@:.ToStorage();
@:
@:storageable.AsInsertable.ExecuteCommand();// 不存在插入
@:storageable.AsUpdateable.UpdateColumns(it => new
@:{
@foreach (var field in updateFields)
{
@: it.@(field),
}
@:}).ExecuteCommand();// 存在更新
@:
@:// 标记错误信息
@:markerErrorAction.Invoke(storageable, pageItems, rows);
@:});
@:});
@:
@:return stream;
@:}
@:}
}
}
@@ -0,0 +1,59 @@
import {useBaseApi} from '/@@/api/base';
// @(Model.BusName)接口服务
export const use@(Model.ClassName)Api = () => {
const baseApi = useBaseApi("@(Model.LowerClassName)");
return {
// 分页查询@(Model.BusName)
page: baseApi.page,
// 查看@(Model.BusName)详细
detail: baseApi.detail,
// 新增@(Model.BusName)
add: baseApi.add,
// 更新@(Model.BusName)
update: baseApi.update,
@if (Model.HasSetStatus) {
@:// 设置@(Model.BusName)状态
@:setStatus: baseApi.setStatus,
}
// 删除@(Model.BusName)
delete: baseApi.delete,
// 批量删除@(Model.BusName)
batchDelete: baseApi.batchDelete,
@if (Model.ImportFieldList.Count > 0) {
@:// 导出@(Model.BusName)数据
@:exportData: baseApi.exportData,
@:// 导入@(Model.BusName)数据
@:importData: baseApi.importData,
@:// 下载@(Model.BusName)数据导入模板
@:downloadTemplate: baseApi.downloadTemplate,
}
@if (Model.DropdownFieldList.Count > 0) {
@:// 获取下拉列表数据
@:getDropdownData: (fromPage: Boolean = false, cancel: boolean = false) => baseApi.dropdownData({ fromPage }, cancel),
}
@foreach (var column in Model.UploadFieldList) {
@:// 上传@(column.ColumnComment)
@:upload@(column.PropertyName): (params: any, cancel: boolean = false) => baseApi.uploadFile(params, 'upload@(column.PropertyName)', cancel),
}
}
}
// @(Model.BusName)实体
export interface @(Model.ClassName) {
@{
var typeMap = new Dictionary<string, string>() {
{ "bool", "boolean" },
{ "int", "number" },
{ "long", "number" },
{ "double", "number" },
{ "float", "number" },
{ "decimal", "number" },
{ "byte", "number" }
};
foreach (var column in Model.TableField) {
@:// @(column.ColumnComment)
@:@(column.LowerPropertyName)@(column.WhetherRequired == "Y" ? "?" : ""): @(Regex.IsMatch("@(column.DataType.Trim('?'))", ".*?Enum") ? "number" : typeMap.GetValueOrDefault(column.DataType.Trim('?').ToLower(), "string"));
}
}
}
@@ -0,0 +1,159 @@
import { h } from 'vue';
import { BasicColumn, FormSchema } from '/@@/components/Table';
@foreach (var column in Model.TableField){
if(column.EffectType == "Upload" || column.EffectType == "Upload_SingleFile"){
@:import { uploadFile } from '/@@/api/sys/admin';
}else if(column.EffectType == "ForeignKey"){
@:import { get@(column.FkEntityName)Dropdown } from '/@@/api/main/@(Model.ClassName)';
}else if(column.EffectType == "DictSelector"){
@:import { getDataList } from '/@@/api/sys/admin';
}else if(column.EffectType == "ApiTreeSelector"){
@:import { get@(column.FkEntityName)Tree } from '/@@/api/main/@(Model.ClassName)';
}else if(column.EffectType == "ConstSelector"){
@:import { codeToName, getSelector } from '/@@/utils/helper/constSelectorHelper';
}else if(column.EffectType == "Switch"){
@:import { Switch } from 'ant-design-vue';
}
}
export const columns: BasicColumn[] = [
@foreach (var column in Model.TableField){
if(column.WhetherTable == "Y"){
@:{
@:title: '@column.ColumnComment',
@:dataIndex: '@column.LowerPropertyName',
@:sorter: true,
if(column.EffectType == "Upload" || column.EffectType == "Upload_SingleFile"){
@:slots: { customRender: '@(column.LowerPropertyName)' },
}else if(column.EffectType == "ForeignKey"){
@:customRender: ({ record }) => {
@:return record.fk@(column.PropertyName).@(column.LowerFkDisplayColumnsList?.First());
@:},
}else if(column.EffectType == "Switch"){
@:customRender: ({ record }) => {
@:return h(@(column.EffectType), { checked: record.@(column.LowerPropertyName) });
@:},
}else if(column.EffectType == "ConstSelector"){
@:customRender: ({ record }) => {
@:return codeToName(record.@(column.LowerPropertyName), '@(column.DictTypeCode)');
@:},
}
@:},
}
}
];
export const searchFormSchema: FormSchema[] = [
@foreach (var column in Model.WhetherQueryList){
@:{
@:field: '@column.LowerPropertyName',
@:label: '@column.ColumnComment',
@:colProps: { span: 8 },
if(column.EffectType == "ForeignKey"){
@:component: 'ApiSelect',
@:componentProps: {
@:api: get@(column.FkEntityName)Dropdown,
@:labelField: 'label',
@:valueField: 'value',
@:},
}else if(column.EffectType == "DictSelector"){
@:component: 'ApiSelect',
@:componentProps: {
@:api: getDataList,
@:params: '@(column.DictTypeCode)',
@:fieldNames: {
@:label: 'label',
@:value: 'value',
@:},
@:},
}else if(column.EffectType == "ConstSelector"){
@:component: 'Select',
@:componentProps: {
@:options: getSelector('@(column.DictTypeCode)'),
@:fieldNames: {
@:label: 'name',
@:value: 'code',
@:},
@:},
}else if(column.EffectType == "ApiTreeSelector"){
@:component: '@(column.EffectType)',
@:componentProps: {
@:api: get@(column.FkEntityName)Tree,
@:},
}
else if(column.NetType?.TrimEnd('?') == "DateTime" && column.QueryType == "~"){
@:component: 'RangePicker',
@:componentProps: {
@: valueFormat:"YYYY-MM-DD"
@:},
} else {
@:component: 'Input',
}
@:},
}
];
export const formSchema: FormSchema[] = [
@foreach (var column in Model.TableField){
@:{
@:label: '@column.ColumnComment',
@:field: '@column.LowerPropertyName',
if(column.EffectType == "ForeignKey"){
@:component: 'ApiSelect',
@:componentProps: {
@:api: get@(column.FkEntityName)Dropdown,
@:labelField: 'label',
@:valueField: 'value',
@:},
}else if(column.EffectType == "DictSelector"){
@:component: 'ApiSelect',
@:componentProps: {
@:api: getDataList,
@:params: '@(column.DictTypeCode)',
@:fieldNames: {
@:label: 'label',
@:value: 'value',
@:},
@:},
}else if(column.EffectType == "ConstSelector"){
@:component: 'Select',
@:componentProps: {
@:options: getSelector('@(column.DictTypeCode)'),
@:fieldNames: {
@:label: 'name',
@:value: 'code',
@:},
@:},
}else if(column.EffectType == "ApiTreeSelector"){
@:component: '@(column.EffectType)',
@:componentProps: {
@:api: get@(column.FkEntityName)Tree,
@:},
}else if(column.EffectType == "Switch"){
@:component: '@(column.EffectType)',
@:componentProps: {
@:checkedChildren: '是',
@:unCheckedChildren: '否',
@:},
}else{
@:component: '@column.EffectType',
}
if(column.WhetherRequired == "Y"){
@:required: true,
}else{
@:required: false,
}
if(column.EffectType == "Upload" || column.EffectType == "Upload_SingleFile"){
@:componentProps: {
@:maxNumber: 1,
@:api: uploadFile,
@:},
}
if(column.LowerPropertyName == "id"){
@:show: false,
}
@:colProps: { span: 12 },
@:},
}
];
@@ -0,0 +1,71 @@
<template>
<BasicModal v-bind="$attrs" @@register="registerModal" :width="700" :title="getTitle" @@ok="handleSubmit">
<BasicForm @@register="registerForm" />
</BasicModal>
</template>
<script lang="ts">
import { defineComponent, ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@@/components/Modal';
import { BasicForm, useForm } from '/@@/components/Form/index';
import { formSchema } from './data.data';
import { add@(Model.ClassName), update@(Model.ClassName) } from '/@@/api/main/@(Model.ClassName)';
export default defineComponent({
components: { BasicModal, BasicForm },
emits: ['success', 'register'],
setup(_, { emit }) {
const isUpdate = ref(true);
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 100,
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: {
span: 23,
},
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
setFieldsValue({
...data.record,
});
@foreach (var column in Model.TableField){
if(column.EffectType == "Upload" || column.EffectType == "Upload_SingleFile"){
@:if (data.record.@(column.LowerPropertyName)) {
@:setFieldsValue({
@:@(column.LowerPropertyName): [data.record.@(column.LowerPropertyName)Attachment],
@:});
@:}
}
}
}
});
const getTitle = computed(() => (!unref(isUpdate) ? '新增@(Model.BusName)' : '编辑@(Model.BusName)'));
async function handleSubmit() {
try {
var values = await validate();
@foreach (var column in Model.TableField){
if(column.EffectType == "Upload" || column.EffectType == "Upload_SingleFile"){
@:if (values.@(column.LowerPropertyName)) {
@:values = { ...values, @(column.LowerPropertyName): values.@(column.LowerPropertyName)[0].id };
@:}
}
}
setModalProps({ confirmLoading: true });
if (!unref(isUpdate)) {
await add@(Model.ClassName)(values);
} else {
await update@(Model.ClassName)(values);
}
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
return { registerModal, registerForm, getTitle, handleSubmit };
},
});
</script>
@@ -0,0 +1,198 @@
<script lang="ts" name="@(Model.LowerClassName)" setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage } from "element-plus";
import type { FormRules } from "element-plus";
@if(Model.TableField.Any(x => x.EffectType == "DatePicker")) {
@:import { formatDate } from '/@@/utils/formatTime';
}
@if(Model.UploadFieldList.Count > 0) {
@:import { Plus,Document } from "@@element-plus/icons-vue";
@:import { UploadRequestOptions } from "element-plus";
}
@if(Model.HasConstField) {
@:import { useUserInfo } from "/@@/stores/userInfo";
}
import { use@(Model.ClassName)Api } from '/@@/api/@(Model.PagePath)/@(Model.LowerClassName)';
@if(@Model.TableField.Any(x=>x.EffectType == "Editor")){
@:import Editor from '/@@/components/editor/index.vue';
}
//父级传递来的函数,用于回调
const emit = defineEmits(["reloadTable"]);
const @(Model.LowerClassName)Api = use@(Model.ClassName)Api();
const ruleFormRef = ref();
const state = reactive({
title: '',
loading: false,
showDialog: false,
ruleForm: {} as any,
stores: @(Model.HasConstField ? "useUserInfo()" : "{}"),
dropdownData: {} as any,
});
// 自行添加其他规则
const rules = ref<FormRules>({
@foreach (var column in Model.AddUpdateFieldList.Where(col => col.WhetherRequired == "Y")) {
var trigger = column.EffectType == "DatePicker" || column.EffectType == "DictSelector" || column.EffectType == "EnumSelector" || column.EffectType == "ApiTreeSelector" ? "change" : "blur";
@:@column.LowerPropertyName: [{required: true, message: '请选择@(column.ColumnComment)!', trigger: '@trigger',},],
}
});
// 页面加载时
onMounted(async () => {
@if (Model.DropdownFieldList.Count > 0) {
@:const data = await @(Model.LowerClassName)Api.getDropdownData(false).then(res => res.data.result) ?? {};
@foreach (var column in Model.DropdownFieldList) {
@:state.dropdownData.@(column.LowerPropertyName) = data.@(column.LowerPropertyName) ?? [];
}
}
});
// 打开弹窗
const openDialog = async (row: any, title: string) => {
state.title = title;
row = row ?? { @(Model.GetAddDefaultValue()) };
state.ruleForm = row.id ? await @(Model.LowerClassName)Api.detail(row.id).then(res => res.data.result) : JSON.parse(JSON.stringify(row));
state.showDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
emit("reloadTable");
state.showDialog = false;
};
// 提交
const submit = async () => {
ruleFormRef.value.validate(async (isValid: boolean, fields?: any) => {
if (isValid) {
let values = state.ruleForm;
await @(Model.LowerClassName)Api[state.ruleForm.@(Model.PrimaryKeyFieldList.First().LowerPropertyName) ? 'update' : 'add'](values);
closeDialog();
} else {
ElMessage({
message: `表单有${Object.keys(fields).length}处验证失败,请修改后再提交`,
type: "error",
});
}
});
};
@foreach (var column in Model.UploadFieldList) {
@:const upload@(column.PropertyName)Handle = async (options: UploadRequestOptions) => {
@:const res = await @(Model.LowerClassName)Api.upload@(column.PropertyName)(options);
@:state.ruleForm.@(column.LowerPropertyName) = res.data.result?.url;
@:};
@:
}
//将属性或者函数暴露给父组件
defineExpose({ openDialog });
</script>
<template>
<div class="@(Model.LowerClassName)-container">
<el-dialog v-model="state.showDialog" :width="800" draggable :close-on-click-modal="false">
<template #header>
<div style="color: #fff">
<span>{{ state.title }}</span>
</div>
</template>
<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules">
<el-row :gutter="35">
@foreach (var column in Model.PrimaryKeyFieldList) {
@:<el-form-item v-show="false">
@:<el-input v-model="state.ruleForm.@(column.LowerPropertyName)" />
@:</el-form-item>
}
@foreach (var column in Model.AddUpdateFieldList) {
var showStatus = Model.IsStatus(column) ? $"v-if=\"state.ruleForm.{Model.PrimaryKeyFieldList.First().LowerPropertyName}\" " : "";
if (column.EffectType == "InputTextArea"||column.EffectType == "Editor"){
@:<el-col :xs="24" class="mb20" @showStatus>
} else{
@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" @showStatus>
}
@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
if (column.IsSelectorEffectType) {
if (column.EffectType == "ApiTreeSelector") {
@:<el-cascader
@::options="state.dropdownData.@(column.LowerPropertyName)"
@::props="{ checkStrictly: true, emitPath: false }"
@:v-model="state.ruleForm.@(column.LowerPropertyName)"
@:placeholder="请选择@(column.ColumnComment)"
@:clearable
@:filterable
@:class="w100">
@:<template #default="{ node, data }">
@:<span>{{ data.label }}</span>
@:<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
@:</template>
@:</el-cascader>
} else if (column.EffectType == "DictSelector" || column.EffectType == "EnumSelector") {
@:<g-sys-dict v-model="state.ruleForm.@(column.LowerPropertyName)" code="@(column.DictTypeCode)" render-as="select" placeholder="请选择@(column.ColumnComment)" clearable filterable />
} else {
@:<el-select clearable filterable v-model="state.ruleForm.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
if (column.EffectType == "ForeignKey") {
@:<el-option v-for="(item,index) in state.dropdownData.@(column.LowerPropertyName)" :key="index" :value="item.value" :label="item.label" />
} else if (column.EffectType == "ConstSelector") {
@:<el-option v-for="(item, index) in state.stores.getConstDataByTypeCode('@column.DictTypeCode')" :key="index" :label="item.name" :value="item.code" />
}
@:</el-select>
}
} else if (column.EffectType == "Upload") {
@:<el-upload
@:list-type="picture-card"
@::show-file-list="false"
@::http-request="upload@(column.PropertyName)Handle">
@:<img
@:v-if="state.ruleForm.@(column.LowerPropertyName)"
@::src="state.ruleForm.@(column.LowerPropertyName)"
@:@@click="state.ruleForm.@(column.LowerPropertyName)=''"
@:style="width: 100%; height: 100%; object-fit: contain"/>
@:<el-icon v-else><Plus /></el-icon>
@:</el-upload>
}else if (column.EffectType == "Upload_SingleFile") {
@:<el-upload
@::show-file-list="false"
@::http-request="upload@(column.PropertyName)Handle">
@:<template #trigger="">
@:<el-button class="mr10" type="primary" icon="ele-MostlyCloudy">选择文件</el-button>
@:</template>
@:</el-upload>
@:<a
@::href="state.ruleForm.@(column.LowerPropertyName)" target="_blank"
@:v-if="state.ruleForm.@(column.LowerPropertyName)">
@:<el-icon class="mr5"><Document /></el-icon>
@:<span>{{ state.ruleForm.@(column.LowerPropertyName).split('/').pop() }}</span>
@:</a>
}
else if (column.EffectType == "InputNumber") {
@:<el-input-number v-model="state.ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" clearable />
} else if (column.EffectType == "Switch") {
@:<el-switch v-model="state.ruleForm.@(column.LowerPropertyName)" />
} else if (column.EffectType == "DatePicker") {
@:<el-date-picker v-model="state.ruleForm.@(column.LowerPropertyName)" type="date" placeholder="@(column.ColumnComment)" />
}else if(@column.EffectType == "Editor"){
@:<Editor v-model:get-html="state.ruleForm.@(@column.LowerPropertyName)" />
} else {
var inputType = column.EffectType == "InputTextArea" ? "type=\"textarea\" " : "";
var maxlength = column.ColumnLength > 0 ? $"maxlength=\"{column.ColumnLength}\" " : "";
@:<el-input v-model="state.ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" @(maxlength)@(inputType)show-word-limit clearable />
}
@:</el-form-item>
@:</el-col>
}
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @@click="() => state.showDialog = false">取 消</el-button>
<el-button @@click="submit" type="primary" v-reclick="1000">确 定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
:deep(.el-select), :deep(.el-input-number) {
width: 100%;
}
</style>
@@ -0,0 +1,326 @@
<script lang="ts" setup name="@(Model.LowerClassName)">
import { ref, reactive, onMounted } from "vue";
import { auth } from '/@@/utils/authFunction';
import { ElMessageBox, ElMessage } from "element-plus";
import { downloadStreamFile } from "/@@/utils/download";
@if(Model.PrintType == "custom") {
@:// 推荐设置操作 width 为 200
@:import { hiprint } from 'vue-plugin-hiprint';
@:import { getAPI } from '/@@/utils/axios-utils';
@:import { SysPrintApi } from '/@@/api-services/api';
@:import { SysPrint } from '/@@/api-services/models';
@:import { formatDate } from '/@@/utils/formatTime';
}
import { use@(Model.ClassName)Api } from '/@@/api/@(Model.PagePath)/@(Model.LowerClassName)';
@if(Model.HasConstField) {
@:import { useUserInfo } from "/@@/stores/userInfo";
}
import editDialog from '/@@/views/@(Model.PagePath)/@(Model.LowerClassName)/component/editDialog.vue'
import printDialog from '/@@/views/system/print/component/hiprint/preview.vue'
import ModifyRecord from '/@@/components/table/modifyRecord.vue';
@if(Model.ImportFieldList.Count > 0) {
@:import ImportData from "/@@/components/table/importData.vue";
}
const @(Model.LowerClassName)Api = use@(Model.ClassName)Api();
const printDialogRef = ref();
const editDialogRef = ref();
@if (Model.ImportFieldList.Count > 0) {
@:const importDataRef = ref();
}
const state = reactive({
exportLoading: false,
tableLoading: false,
stores: @(Model.HasConstField ? "useUserInfo()" : "{}"),
showAdvanceQueryUI: @(Model.HasLikeQuery ? "false" : "true"),
dropdownData: {} as any,
selectData: [] as any[],
tableQueryParams: {} as any,
tableParams: {
page: 1,
pageSize: 20,
total: 0,
field: 'createTime', // 默认的排序字段
order: 'descending', // 排序方向
descStr: 'descending', // 降序排序的关键字符
},
tableData: [],
});
// 页面加载时
onMounted(async () => {
@if (Model.DropdownFieldList.Count > 0) {
@:const data = await @(Model.LowerClassName)Api.getDropdownData(true).then(res => res.data.result) ?? {};
@foreach (var column in Model.DropdownFieldList) {
@:state.dropdownData.@(column.LowerPropertyName) = data.@(column.LowerPropertyName);
}
}
});
// 查询操作
const handleQuery = async (params: any = {}) => {
state.tableLoading = true;
state.tableParams = Object.assign(state.tableParams, params);
const result = await @(Model.LowerClassName)Api.page(Object.assign(state.tableQueryParams, state.tableParams)).then(res => res.data.result);
state.tableParams.total = result?.total;
state.tableData = result?.items ?? [];
state.tableLoading = false;
};
// 列排序
const sortChange = async (column: any) => {
state.tableParams.field = column.prop;
state.tableParams.order = column.order;
await handleQuery();
};
// 删除
const del@(Model.ClassName) = (row: any) => {
ElMessageBox.confirm(`确定要删除吗?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
await @(Model.LowerClassName)Api.delete({ @(Model.PrimaryKeysFormat(", ", "{0}: row.{0}", true)) });
handleQuery();
ElMessage.success("删除成功");
}).catch(() => {});
};
// 批量删除
const batchDel@(Model.ClassName) = () => {
ElMessageBox.confirm(`确定要删除${state.selectData.length}条记录吗?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
await @(Model.LowerClassName)Api.batchDelete(state.selectData.map(u => ({ @(Model.PrimaryKeysFormat(", ", "{0}: u.{0}", true)) }) )).then(res => {
ElMessage.success(`成功批量删除${res.data.result}条记录`);
handleQuery();
});
}).catch(() => {});
};
@if(Model.PrintType == "custom") {
@:
@:// 打开打印页面
@:const openPrint@(Model.ClassName) = async (row: any) => {
@:var res = await getAPI(SysPrintApi).apiSysPrintPrintNameGet('@Model.PrintName');
@:var printTemplate = res.data.result as SysPrint;
@:var template = JSON.parse(printTemplate.template);
@:row['printDate'] = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS')
@:printDialogRef.value.showDialog(new hiprint.PrintTemplate({template: template}), row, template.panels[0].width);
@:}
}
@if (Model.HasSetStatus) {
@:
@:// 设置状态
@:const change@(Model.ClassName)Status = async (row: any) => {
@:await @(Model.LowerClassName)Api.setStatus({ @(Model.PrimaryKeysFormat(", ", "{0}: row.{0}", true)), status: row.status }).then(() => ElMessage.success('状态设置成功'));
@:};
}
@if (Model.ImportFieldList.Count > 0) {
@:
@:// 导出数据
@:const export@(Model.ClassName)Command = async (command: string) => {
@:try {
@:state.exportLoading = true;
@:if (command === 'select') {
@:const params = Object.assign({}, state.tableQueryParams, state.tableParams, { selectKeyList: state.selectData.map(u => u.@(Model.PrimaryKeyFieldList.First().LowerPropertyName)) });
@:await @(Model.LowerClassName)Api.exportData(params).then(res => downloadStreamFile(res));
@:} else if (command === 'current') {
@:const params = Object.assign({}, state.tableQueryParams, state.tableParams);
@:await @(Model.LowerClassName)Api.exportData(params).then(res => downloadStreamFile(res));
@:} else if (command === 'all') {
@:const params = Object.assign({}, state.tableQueryParams, state.tableParams, { page: 1, pageSize: 99999999 });
@:await @(Model.LowerClassName)Api.exportData(params).then(res => downloadStreamFile(res));
@:}
@:} finally {
@:state.exportLoading = false;
@:}
@:}
}
handleQuery();
</script>
<template>
<div class="@(Model.LowerClassName)-container" v-loading="state.exportLoading">
<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
<el-form :model="state.tableQueryParams" ref="queryForm" labelWidth="90">
<el-row>
@if(Model.QueryWhetherList.Count > 0) {
if(Model.HasLikeQuery) {
@:<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
@:<el-form-item label="关键字">
@:<el-input v-model="state.tableQueryParams.keyword" clearable placeholder="请输入模糊查询关键字"/>
@:</el-form-item>
@:</el-col>
}
foreach (var column in Model.QueryWhetherList) {
@:<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="state.showAdvanceQueryUI">
@:<el-form-item label="@column.ColumnComment">
if(column.EffectType == "Input" || column.EffectType == "InputTextArea"){
@:<el-input v-model="state.tableQueryParams.@(column.LowerPropertyName)" clearable placeholder="请输入@(column.ColumnComment)"/>
}else if(column.EffectType == "InputTextArea"){
@:<el-input-number v-model="state.tableQueryParams.@(column.LowerPropertyName)" clearable placeholder="请输入@(column.ColumnComment)"/>
}else if(column.EffectType == "InputNumber"){
@:<el-input-number v-model="state.tableQueryParams.@(column.LowerPropertyName)" clearable placeholder="请输入@(column.ColumnComment)"/>
}else if(column.IsSelectorEffectType || column.EffectType == "ForeignKey") {
if (column.EffectType == "DictSelector" || column.EffectType == "EnumSelector") {
@:<g-sys-dict v-model="state.tableQueryParams.@(column.LowerPropertyName)" code="@(column.DictTypeCode)" render-as="select" placeholder="请选择@(column.ColumnComment)" clearable filterable />
} else {
@:<el-select clearable filterable v-model="state.tableQueryParams.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
if (column.EffectType == "ForeignKey") {
@:<el-option v-for="(item,index) in state.dropdownData.@(column.LowerPropertyName) ?? []" :key="index" :value="item.value" :label="item.label" />
} else if (column.EffectType == "ConstSelector") {
@:<el-option v-for="(item, index) in state.stores.getConstDataByTypeCode('@column.DictTypeCode')" :key="index" :label="item.name" :value="item.code" />
}
@:</el-select>
}
}else if(column.EffectType == "ApiTreeSelector"){
@:<el-cascader
@::options="state.dropdownData.@(column.LowerPropertyName) ?? []"
@:@:props="{ checkStrictly: true, emitPath: false }"
@:placeholder="请选择@(column.ColumnComment)"
@:clearable
@:filterable
@:class="w100"
@:v-model="state.tableQueryParams.@(column.LowerPropertyName)"
@:>
@:<template #default="{ node, data }">
@:<span>{{ data.label }}</span>
@:<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
@:</template>
@:</el-cascader>
} else if (column.EffectType == "DatePicker"){
if (column.QueryType == "~") {
@:<el-date-picker type="daterange" v-model="state.tableQueryParams.@(column.LowerPropertyName)Range" value-format="YYYY-MM-DD HH:mm:ss" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" />
} else {
@:<el-date-picker placeholder="请选择@(column.ColumnComment)" value-format="YYYY/MM/DD" v-model="state.tableQueryParams.@(column.LowerPropertyName)" />
}
} else if (column.EffectType == "Switch"){
@:<el-select clearable filterable v-model="state.tableQueryParams.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
@:<el-option value="true" label="是" />
@:<el-option value="false" label="否" />
@:</el-select>
} else {
@:<el-input v-model="state.tableQueryParams.@(column.LowerPropertyName)" clearable placeholder="请输入@(column.ColumnComment)"/>
}
@:</el-form-item>
@:</el-col>
}
}
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
<el-form-item @(Model.QueryWhetherList.Count > 0 ? "" : "label-width=\"0px\"")>
<el-button-group style="display: flex; align-items: center;">
<el-button type="primary" icon="ele-Search" @@click="handleQuery" v-auth="'@(Model.LowerClassName):page'" v-reclick="1000"> @(Model.QueryWhetherList.Count > 0 ? "查询" : "刷新") </el-button>
@if (Model.QueryWhetherList.Count > 0) {
@:<el-button icon="ele-Refresh" @@click="() => state.tableQueryParams = {}"> 重置 </el-button>
@if (Model.HasLikeQuery) {
@:<el-button icon="ele-ZoomIn" @@click="() => state.showAdvanceQueryUI = true" v-if="!state.showAdvanceQueryUI" style="margin-left:5px;"> 高级查询 </el-button>
@:<el-button icon="ele-ZoomOut" @@click="() => state.showAdvanceQueryUI = false" v-if="state.showAdvanceQueryUI" style="margin-left:5px;"> 隐藏 </el-button>
}
}
<el-button type="danger" style="margin-left:5px;" icon="ele-Delete" @@click="batchDel@(Model.ClassName)" :disabled="state.selectData.length == 0" v-auth="'@(Model.LowerClassName):batchDelete'"> 删除 </el-button>
<el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @@click="editDialogRef.openDialog(null, '新增@(Model.BusName)')" v-auth="'@(Model.LowerClassName):add'"> 新增 </el-button>
@if (Model.ImportFieldList.Count > 0) {
@:<el-dropdown :show-timeout="70" :hide-timeout="50" @@command="export@(Model.ClassName)Command">
@:<el-button type="primary" style="margin-left:5px;" icon="ele-FolderOpened" v-reclick="20000" v-auth="'@(Model.LowerClassName):export'"> 导出 </el-button>
@:<template #dropdown>
@:<el-dropdown-menu>
@:<el-dropdown-item command="select" :disabled="state.selectData.length == 0">导出选中</el-dropdown-item>
@:<el-dropdown-item command="current">导出本页</el-dropdown-item>
@:<el-dropdown-item command="all">导出全部</el-dropdown-item>
@:</el-dropdown-menu>
@:</template>
@:</el-dropdown>
@:<el-button type="warning" style="margin-left:5px;" icon="ele-MostlyCloudy" @@click="importDataRef.openDialog()" v-auth="'@(Model.LowerClassName):import'"> 导入 </el-button>
}
</el-button-group>
</el-form-item>
</el-col>
</el-row>
@* 操作区另起一行
@:<el-row>
@:<el-col>
@:<el-button-group style="margin-left:20px;margin-bottom:5px;">
@:<el-button type="primary" icon="ele-Plus" @@click="editDialogRef.openDialog(null, '新增@(Model.BusName)')" v-auth="'@(Model.LowerClassName):add'"> 新增 </el-button>
</el-button-group>
@:</el-col>
@:</el-row>
*@
</el-form>
</el-card>
<el-card class="full-table" shadow="hover" style="margin-top: 5px">
<el-table :data="state.tableData" @@selection-change="(val: any[]) => { state.selectData = val; }" style="width: 100%" v-loading="state.tableLoading" tooltip-effect="light" row-key="@Model.PrimaryKeyFieldList.First().LowerPropertyName" @@sort-change="sortChange" border>
<el-table-column type="selection" width="40" align="center" v-if="auth('@(Model.LowerClassName):batchDelete') || auth('@(Model.LowerClassName):export')" />
<el-table-column type="index" label="序号" width="55" align="center"/>
@foreach (var column in Model.TableField.Where(u => u.WhetherTable == "Y")){
if(column.EffectType == "DictSelector" || column.EffectType == "EnumSelector" || column.EffectType == "Upload" || @column.EffectType == "Switch") {
@:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) show-overflow-tooltip>
@:<template #default="scope">
if (column.EffectType == "Upload") {
@:<el-image
@:v-if="scope.row.@column.LowerPropertyName"
@:style="width: 60px; height: 60px"
@::src="scope.row.@column.LowerPropertyName"
@::lazy="true"
@::hide-on-click-modal="true"
@::preview-src-list="[scope.row.@column.LowerPropertyName]"
@::initial-index="0"
@:fit="scale-down"
@:preview-teleported />
} else if (column.EffectType == "Switch") {
@:<el-tag v-if="scope.row.@(column.LowerPropertyName)"> 是 </el-tag>
@:<el-tag type="danger" v-else> 否 </el-tag>
} else if (Model.IsStatus(column)) {
@:<el-switch v-model="scope.row.@column.LowerPropertyName" :active-value="1" :inactive-value="2" size="small" @@change="change@(Model.ClassName)Status(scope.row)" />
} else {
@:<g-sys-dict v-model="scope.row.@column.LowerPropertyName" code="@(column.DictTypeCode)" />
}
@:</template>
@:</el-table-column>
} else if (column.EffectType == "ConstSelector" || column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
var formatter = column.EffectType == "ConstSelector" ? $"state.stores.getConstItemNameByType('{column.DictTypeCode}', row.{column.LowerPropertyName})" : $"row.{column.LowerExtendedPropertyName}";
@:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) :formatter="(row: any) => @(formatter)" show-overflow-tooltip />
} else {
@:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) show-overflow-tooltip />
}
}
<el-table-column label="修改记录" width="100" align="center" show-overflow-tooltip>
<template #default="scope">
<ModifyRecord :data="scope.row" />
</template>
</el-table-column>
<el-table-column label="操作" width="@(Model.PrintType == "custom" ? "200" : "140")" align="center" fixed="right" show-overflow-tooltip v-if="auth('@(Model.LowerClassName):update') || auth('@(Model.LowerClassName):delete')">
<template #default="scope">
@if (Model.PrintType == "custom") {
@:<el-button icon="ele-Printer" size="small" text type="primary" @@click="openPrint@(Model.ClassName)(scope.row)" v-auth="'@(Model.LowerClassName):print'"> 打印 </el-button>
}
<el-button icon="ele-Edit" size="small" text type="primary" @@click="editDialogRef.openDialog(scope.row, '编辑@(Model.BusName)')" v-auth="'@(Model.LowerClassName):update'"> 编辑 </el-button>
<el-button icon="ele-Delete" size="small" text type="primary" @@click="del@(Model.ClassName)(scope.row)" v-auth="'@(Model.LowerClassName):delete'"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="state.tableParams.page"
v-model:page-size="state.tableParams.pageSize"
@@size-change="(val: any) => handleQuery({ pageSize: val })"
@@current-change="(val: any) => handleQuery({ page: val })"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 50, 100, 200, 500]"
:total="state.tableParams.total"
size="small"
background />
@if (Model.ImportFieldList.Count > 0) {
@:<ImportData ref="importDataRef" :import="@(Model.LowerClassName)Api.importData" :download="@(Model.LowerClassName)Api.downloadTemplate" v-auth="'@(Model.LowerClassName):import'" @@refresh="handleQuery"/>
}
<printDialog ref="printDialogRef" :title="'打印@(Model.BusName)'" @@reloadTable="handleQuery" />
<editDialog ref="editDialogRef" @@reloadTable="handleQuery" />
</el-card>
</div>
</template>
<style scoped>
:deep(.el-input), :deep(.el-select), :deep(.el-input-number) {
width: 100%;
}
</style>