diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN.Abp.Cli.csproj b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN.Abp.Cli.csproj index 671afeaa8..e948a6227 100644 --- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN.Abp.Cli.csproj +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN.Abp.Cli.csproj @@ -1,20 +1,25 @@  - - - Exe - net7.0 - 6.0.2 - colin - Use LINGYUN.MicroService.Templates command line - true - labp - ./nupkg - MIT - https://github.com/colinin/abp-next-admin - - + + + Exe + net7.0 + 7.0.0 + colin + Use LINGYUN.MicroService.Templates command line + true + labp + ./nupkg + MIT + https://github.com/colinin/abp-next-admin + + + + + + + @@ -26,6 +31,7 @@ + diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs index 2d8f1bea4..82776be13 100644 --- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs @@ -1,27 +1,38 @@ using LINGYUN.Abp.Cli.Commands; using LINGYUN.Abp.Cli.ServiceProxying.CSharp; using LINGYUN.Abp.Cli.ServiceProxying.TypeScript; +using LINGYUN.Abp.Cli.UI; +using LINGYUN.Abp.Cli.UI.Vben; using Volo.Abp.Autofac; using Volo.Abp.Cli; using Volo.Abp.Cli.ServiceProxying; using Volo.Abp.Modularity; +using Volo.Abp.TextTemplating.Scriban; +using Volo.Abp.VirtualFileSystem; namespace LINGYUN.Abp.Cli { [DependsOn( typeof(AbpCliCoreModule), + typeof(AbpTextTemplatingScribanModule), typeof(AbpAutofacModule) )] public class AbpCliModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + Configure(options => { options.Commands.Clear(); options.Commands[HelpCommand.Name] = typeof(HelpCommand); options.Commands[CreateCommand.Name] = typeof(CreateCommand); options.Commands[GenerateProxyCommand.Name] = typeof(GenerateProxyCommand); + options.Commands[GenerateViewCommand.Name] = typeof(GenerateViewCommand); }); Configure(options => @@ -37,6 +48,11 @@ namespace LINGYUN.Abp.Cli options.ScriptGenerators[VbenDynamicHttpApiScriptGenerator.Name] = new VbenDynamicHttpApiScriptGenerator(); options.ScriptGenerators[UniAppAxiosHttpApiScriptGenerator.Name] = new UniAppAxiosHttpApiScriptGenerator(); }); + + Configure(options => + { + options.Generators[VbenViewGenerator.Name] = typeof(VbenViewGenerator); + }); } } } diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs index a76c5daaf..5a581ae59 100644 --- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs @@ -66,7 +66,7 @@ public class GenerateProxyCommand : IConsoleCommand, ITransientDependency var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long); var workDirectory = commandLineArgs.Options.GetOrNull(Options.WorkDirectory.Short, Options.WorkDirectory.Long) ?? Directory.GetCurrentDirectory(); var folder = commandLineArgs.Options.GetOrNull(Options.Folder.Long); - var serviceTypeArg = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.ServiceType.Long); + var serviceTypeArg = commandLineArgs.Options.GetOrNull(Options.ServiceType.Short, Options.ServiceType.Long); ServiceType? serviceType = null; if (!serviceTypeArg.IsNullOrWhiteSpace()) diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateViewCommand.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateViewCommand.cs new file mode 100644 index 000000000..4296a1820 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateViewCommand.cs @@ -0,0 +1,139 @@ +using LINGYUN.Abp.Cli.UI; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Cli; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Commands; +using Volo.Abp.Cli.ServiceProxying; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.Cli.Commands; +public class GenerateViewCommand : IConsoleCommand, ITransientDependency +{ + public const string Name = "generate-view"; + + protected string CommandName => Name; + + protected AbpCliViewGeneratorOptions ViewGeneratorOptions { get; } + + protected IServiceScopeFactory ServiceScopeFactory { get; } + + public GenerateViewCommand( + IOptions viewGeneratorOptions, + IServiceScopeFactory serviceScopeFactory) + { + ServiceScopeFactory = serviceScopeFactory; + ViewGeneratorOptions = viewGeneratorOptions.Value; + } + + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + var generateType = commandLineArgs.Options.GetOrNull(Options.GenerateType.Short, Options.GenerateType.Long)?.ToUpper(); + + if (!ViewGeneratorOptions.Generators.ContainsKey(generateType)) + { + throw new CliUsageException("Option Type value is invalid" + + Environment.NewLine + + GetUsageInfo()); + } + + using var scope = ServiceScopeFactory.CreateScope(); + var generatorType = ViewGeneratorOptions.Generators[generateType]; + var serviceProxyGenerator = scope.ServiceProvider.GetService(generatorType).As(); + + await serviceProxyGenerator.GenerateAsync(BuildArgs(commandLineArgs)); + } + + private GenerateViewArgs BuildArgs(CommandLineArgs commandLineArgs) + { + var url = commandLineArgs.Options.GetOrNull(Options.Url.Short, Options.Url.Long); + var output = commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long); + var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? "app"; + var serviceTypeArg = commandLineArgs.Options.GetOrNull(Options.ServiceType.Short, Options.ServiceType.Long); + + ServiceType? serviceType = null; + if (!serviceTypeArg.IsNullOrWhiteSpace()) + { + serviceType = serviceTypeArg.ToLower() == "application" + ? ServiceType.Application + : serviceTypeArg.ToLower() == "integration" + ? ServiceType.Integration + : null; + } + + return new GenerateViewArgs( + module, + url, + output, + serviceType); + } + + public string GetShortDescription() + { + return "Generate the view code from the http api proxy."; + } + + public string GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(""); + sb.AppendLine($" labp {CommandName}"); + sb.AppendLine(""); + sb.AppendLine("Options:"); + sb.AppendLine(""); + sb.AppendLine("-t Generate ui script(vben-view、vben-model)."); + sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); + + sb.AppendLine(""); + sb.AppendLine("Examples:"); + sb.AppendLine(""); + + sb.AppendLine(" labp generate-proxy -t vben-view -m identity -o api/identity -url https://localhost:44302/"); + + return sb.ToString(); + } + + public static class Options + { + public static class GenerateType + { + public const string Short = "t"; + public const string Long = "type"; + } + + public static class Module + { + public const string Short = "m"; + public const string Long = "module"; + } + + public static class Output + { + public const string Short = "o"; + public const string Long = "output"; + } + + public static class Folder + { + public const string Long = "folder"; + } + + public static class Url + { + public const string Short = "u"; + public const string Long = "url"; + } + + public static class ServiceType + { + public const string Short = "st"; + public const string Long = "service-type"; + } + } +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs index eac4e173a..bd81db0c0 100644 --- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs @@ -86,7 +86,12 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase x.Controllers)) { diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/AbpCliViewGeneratorOptions.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/AbpCliViewGeneratorOptions.cs new file mode 100644 index 000000000..d0e113615 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/AbpCliViewGeneratorOptions.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.Cli.UI; +public class AbpCliViewGeneratorOptions +{ + public IDictionary Generators { get; } + + public AbpCliViewGeneratorOptions() + { + Generators = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/GenerateViewArgs.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/GenerateViewArgs.cs new file mode 100644 index 000000000..1501ed0cf --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/GenerateViewArgs.cs @@ -0,0 +1,26 @@ +using Volo.Abp.Cli.ServiceProxying; + +namespace LINGYUN.Abp.Cli.UI; + +public class GenerateViewArgs +{ + public GenerateViewArgs( + string module, + string url, + string output, + ServiceType? serviceType = null) + { + Module = module; + Url = url; + Output = output; + ServiceType = serviceType; + } + + public string Module { get; } + + public string Output { get; } + + public string Url { get; } + + public ServiceType? ServiceType { get; } +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/IViewGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/IViewGenerator.cs new file mode 100644 index 000000000..f5f85cba4 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/IViewGenerator.cs @@ -0,0 +1,7 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Cli.UI; +public interface IViewGenerator +{ + Task GenerateAsync(GenerateViewArgs args); +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/IVbenModelScriptGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/IVbenModelScriptGenerator.cs new file mode 100644 index 000000000..9bc419120 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/IVbenModelScriptGenerator.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Volo.Abp.Http.Modeling; + +namespace LINGYUN.Abp.Cli.UI.Vben; + +public interface IVbenModelScriptGenerator +{ + Task CreateModel( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel); + + Task CreateTable( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel); +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/IVbenViewScriptGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/IVbenViewScriptGenerator.cs new file mode 100644 index 000000000..e94510ef2 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/IVbenViewScriptGenerator.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Volo.Abp.Http.Modeling; + +namespace LINGYUN.Abp.Cli.UI.Vben; +public interface IVbenViewScriptGenerator +{ + Task CreateModal( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel); + + Task CreateTable( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel); + + Task CreateIndex( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel); +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenModelScriptGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenModelScriptGenerator.cs new file mode 100644 index 000000000..a058a4881 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenModelScriptGenerator.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Modeling; +using Volo.Abp.TextTemplating; + +namespace LINGYUN.Abp.Cli.UI.Vben; + +public class VbenModelScriptGenerator : IVbenModelScriptGenerator, ISingletonDependency +{ + private readonly ITemplateRenderer _templateRenderer; + + public VbenModelScriptGenerator( + ITemplateRenderer templateRenderer) + { + _templateRenderer = templateRenderer; + } + + public async virtual Task CreateModel( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel) + { + var searchModels = new List(); + var inputModels = new List(); + + var moduleDefinition = appModel.Modules + .Where(module => module.Key.Equals(args.Module)) + .Select(module => module.Value) + .FirstOrDefault(); + + var listResultAction = controllerModel.Actions + .Where(action => action.Value.ReturnValue.TypeSimple.Contains("ListResultDto")) + .Select(action => action.Value) + .FirstOrDefault(); + + searchModels.Add( + new ComponentModel + { + Name = "filter", + Component = "Input", + DisplayName = "Search", + ColProps = "{" + + "span: 24" + + "}", + }); + + if (listResultAction != null) + { + foreach (var inputParamter in listResultAction.Parameters) + { + if (appModel.Types.TryGetValue(inputParamter.Type, out var inputModelType)) + { + var span = inputModelType.Properties.Length > 3 ? 8 + : inputModelType.Properties.Length > 2 ? 12 + : 24; + foreach (var inputModelProp in inputModelType.Properties) + { + var component = new ComponentModel + { + Name = inputModelProp.JsonName ?? inputModelProp.Name.ToCamelCase(), + Component = "Input", + DisplayName = "DisplayName:" + inputModelProp.Name.ToPascalCase(), + Required = inputModelProp.IsRequired, + ColProps = "" + + "{" + + $"span: {span}" + + "}", + }; + + if (appModel.Types.TryGetValue(inputModelProp.Type, out var inputModelPropType) && + inputModelPropType.IsEnum) + { + component.Component = "Select"; + + var optionsStr = Environment.NewLine; + + var options = new object[inputModelPropType.EnumNames.Length]; + for (var index = 0; index < inputModelPropType.EnumNames.Length; index++) + { + optionsStr += "" + + "{" + + $"label: {inputModelPropType.EnumNames[index]}," + + $"value: {inputModelPropType.EnumValues[index]}," + + "}" + + "" + Environment.NewLine; + } + component.ComponentProps = "" + + "{" + + $"options: [{optionsStr}]" + + "}"; + } + + searchModels.Add(component); + } + } + } + } + + var createOrUpdateAction = controllerModel.Actions + .Where(action => action.Value.UniqueName.Contains("Create") || action.Value.UniqueName.Contains("Update")) + .Select(action => action.Value) + .FirstOrDefault(); + + if (createOrUpdateAction != null) + { + foreach (var createOrUpdateParamter in createOrUpdateAction.Parameters) + { + if (appModel.Types.TryGetValue(createOrUpdateParamter.Type, out var inputModelType)) + { + foreach (var inputModelProp in inputModelType.Properties) + { + var component = new ComponentModel + { + Name = inputModelProp.JsonName ?? inputModelProp.Name.ToCamelCase(), + Component = "Input", + DisplayName = "DisplayName:" + inputModelProp.Name.ToPascalCase(), + Required = inputModelProp.IsRequired, + ColProps = "" + + "{" + + "span: 24" + + "}", + ComponentProps = "{}" + }; + + inputModels.Add(component); + } + } + } + } + + var modelDataContent = await _templateRenderer.RenderAsync( + "VbenModelData", + new + { + RemoteService = moduleDefinition.RemoteServiceName, + ExistsSearchModels = searchModels.Any(), + SearchModels = searchModels, + InputModels = inputModels, + }); + + return modelDataContent; + } + + public async virtual Task CreateTable( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel) + { + var ouputModels = new List(); + + var moduleDefinition = appModel.Modules + .Where(module => module.Key.Equals(args.Module)) + .Select(module => module.Value) + .FirstOrDefault(); + + var listResultAction = controllerModel.Actions + .Where(action => action.Value.ReturnValue.TypeSimple.Contains("ListResultDto") + || action.Value.ReturnValue.TypeSimple.Contains("PagedResultDto")) + .Select(action => action.Value) + .FirstOrDefault(); + + if (listResultAction != null) + { + foreach (var inputParamter in listResultAction.Parameters) + { + if (appModel.Types.TryGetValue(inputParamter.Type, out var inputModelType)) + { + var span = inputModelType.Properties.Length > 3 ? 8 + : inputModelType.Properties.Length > 2 ? 12 + : 24; + foreach (var inputModelProp in inputModelType.Properties) + { + var component = new ComponentModel + { + Name = inputModelProp.JsonName ?? inputModelProp.Name.ToCamelCase(), + DisplayName = "DisplayName:" + inputModelProp.Name.ToPascalCase() + }; + + ouputModels.Add(component); + } + } + } + } + + var tableDataContent = await _templateRenderer.RenderAsync( + "VbenTableData", + new + { + RemoteService = moduleDefinition.RemoteServiceName, + OuputModels = ouputModels, + }); + + return tableDataContent; + } +} + +public class ComponentModel +{ + public string Name { get; set; } + public string Component { get; set; } + public string DisplayName { get; set; } + public object ColProps { get; set; } + public object ComponentProps { get; set; } + public string Align { get; set; } = "left"; + public int Width { get; set; } = 120; + public bool Sorter { get; set; } = true; + public bool Resizable { get; set; } = true; + public bool Required { get; set; } = false; +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenScriptTemplateDefinitionProvider.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenScriptTemplateDefinitionProvider.cs new file mode 100644 index 000000000..9a66c45ce --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenScriptTemplateDefinitionProvider.cs @@ -0,0 +1,50 @@ +using Volo.Abp.Localization; +using Volo.Abp.TextTemplating; +using static Volo.Abp.Cli.Commands.AddPackageCommand.Options; + +namespace LINGYUN.Abp.Cli.UI.Vben; + +public class VbenScriptTemplateDefinitionProvider : TemplateDefinitionProvider +{ + public override void Define(ITemplateDefinitionContext context) + { + context.Add(CreateCliTemplates()); + } + + private static TemplateDefinition[] CreateCliTemplates() + { + return new[] + { + new TemplateDefinition( + "VbenModelData", + typeof(DefaultResource), + L("Templates:VbenModelData") + ).WithVirtualFilePath("/LINGYUN/Abp/Cli/UI/Vben/Templates/VbenModelDataScript.tpl", true), + new TemplateDefinition( + "VbenModalView", + typeof(DefaultResource), + L("Templates:VbenModalView") + ).WithVirtualFilePath("/LINGYUN/Abp/Cli/UI/Vben/Templates/VbenModalViewScript.tpl", true), + new TemplateDefinition( + "VbenTableData", + typeof(DefaultResource), + L("Templates:VbenTableData") + ).WithVirtualFilePath("/LINGYUN/Abp/Cli/UI/Vben/Templates/VbenTableDataScript.tpl", true), + new TemplateDefinition( + "VbenTableView", + typeof(DefaultResource), + L("Templates:VbenTableView") + ).WithVirtualFilePath("/LINGYUN/Abp/Cli/UI/Vben/Templates/VbenTableViewScript.tpl", true), + new TemplateDefinition( + "VbenComponentIndex", + typeof(DefaultResource), + L("Templates:VbenComponentIndex") + ).WithVirtualFilePath("/LINGYUN/Abp/Cli/UI/Vben/Templates/VbenComponentIndexScript.tpl", true), + }; + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenViewGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenViewGenerator.cs new file mode 100644 index 000000000..8a0a885f7 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenViewGenerator.cs @@ -0,0 +1,111 @@ +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Threading.Tasks; +using Volo.Abp.Cli.Http; +using Volo.Abp.Cli.ServiceProxying; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Modeling; +using Volo.Abp.IO; +using Volo.Abp.Json; + +namespace LINGYUN.Abp.Cli.UI.Vben; +public class VbenViewGenerator : ViewGeneratorBase, ISingletonDependency +{ + public const string Name = "Vben-View"; + + protected IVbenModelScriptGenerator ModelScriptGenerator { get; } + protected IVbenViewScriptGenerator ViewScriptGenerator { get; } + public VbenViewGenerator( + CliHttpClientFactory cliHttpClientFactory, + IJsonSerializer jsonSerializer, + IVbenModelScriptGenerator modelScriptGenerator, + IVbenViewScriptGenerator viewScriptGenerator) + : base(cliHttpClientFactory, jsonSerializer) + { + ModelScriptGenerator = modelScriptGenerator; + ViewScriptGenerator = viewScriptGenerator; + } + + private async Task CreateAndSaveSciptToDisk(string scriptFile, string script) + { + Logger.LogInformation($"The script output file: {scriptFile}."); + Logger.LogInformation($"Saving script: {scriptFile}."); + + FileHelper.DeleteIfExists(scriptFile); + + await File.AppendAllTextAsync(scriptFile, script); + + Logger.LogInformation($"Saved script: {scriptFile} has successful."); + } + + public async override Task GenerateAsync(GenerateViewArgs args) + { + var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync( + args, + new ApplicationApiDescriptionModelRequestDto + { + IncludeTypes = true + }); + var outputFolderRoot = args.Output; + + foreach (var module in applicationApiDescriptionModel.Modules) + { + Logger.LogInformation($"Generating script with remote service: {module.Value.RemoteServiceName}."); + + foreach (var controller in module.Value.Controllers) + { + var componentScriptPath = Path.Combine( + outputFolderRoot, + module.Value.RemoteServiceName.ToKebabCase(), + controller.Value.ControllerName.ToKebabCase()); + + var modelScriptPath = Path.Combine(componentScriptPath, "datas"); + DirectoryHelper.CreateIfNotExists(modelScriptPath); + + await CreateAndSaveSciptToDisk( + Path.Combine(modelScriptPath, "ModalData.ts"), + await ModelScriptGenerator.CreateModel( + args, + applicationApiDescriptionModel, + controller.Value)); + + await CreateAndSaveSciptToDisk( + Path.Combine(modelScriptPath, "TableData.ts"), + await ModelScriptGenerator.CreateTable( + args, + applicationApiDescriptionModel, + controller.Value)); + + var viewScriptPath = Path.Combine(componentScriptPath, "components"); + DirectoryHelper.CreateIfNotExists(viewScriptPath); + + await CreateAndSaveSciptToDisk( + Path.Combine(viewScriptPath, $"{controller.Value.ControllerName.ToPascalCase()}Modal.vue"), + await ViewScriptGenerator.CreateModal( + args, + applicationApiDescriptionModel, + controller.Value)); + + await CreateAndSaveSciptToDisk( + Path.Combine(viewScriptPath, $"{controller.Value.ControllerName.ToPascalCase()}Table.vue"), + await ViewScriptGenerator.CreateTable( + args, + applicationApiDescriptionModel, + controller.Value)); + + await CreateAndSaveSciptToDisk( + Path.Combine(componentScriptPath, "index.vue"), + await ViewScriptGenerator.CreateIndex( + args, + applicationApiDescriptionModel, + controller.Value)); + } + } + } + + protected override ServiceType? GetDefaultServiceType(GenerateViewArgs args) + { + return ServiceType.Application; + } +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenViewScriptGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenViewScriptGenerator.cs new file mode 100644 index 000000000..fd5543eff --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Vben/VbenViewScriptGenerator.cs @@ -0,0 +1,103 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Modeling; +using Volo.Abp.TextTemplating; + +namespace LINGYUN.Abp.Cli.UI.Vben; +public class VbenViewScriptGenerator : IVbenViewScriptGenerator, ISingletonDependency +{ + private readonly ITemplateRenderer _templateRenderer; + + public VbenViewScriptGenerator( + ITemplateRenderer templateRenderer) + { + _templateRenderer = templateRenderer; + } + + public async virtual Task CreateModal( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel) + { + var moduleDefinition = appModel.Modules + .Where(module => module.Key.Equals(args.Module)) + .Select(module => module.Value) + .FirstOrDefault(); + + var modalContent = await _templateRenderer.RenderAsync( + "VbenModalView", + new + { + Application = controllerModel.ControllerName, + ApiPath = $"/@/api/{moduleDefinition.RemoteServiceName.ToKebabCase()}/{controllerModel.ControllerName.ToKebabCase()}", + RemoteService = moduleDefinition.RemoteServiceName, + }); + + return modalContent; + } + + public async virtual Task CreateTable( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel) + { + var moduleDefinition = appModel.Modules + .Where(module => module.Key.Equals(args.Module)) + .Select(module => module.Value) + .FirstOrDefault(); + + var pagedResultAction = controllerModel.Actions + .Where(action => action.Value.ReturnValue.TypeSimple.Contains("PagedResultDto")) + .Select(action => action.Value) + .FirstOrDefault(); + + var deleteAction = controllerModel.Actions + .Where(action => action.Value.Name.Contains("DeleteAsync")) + .Select(action => action.Value) + .FirstOrDefault(); + var updateAction = controllerModel.Actions + .Where(action => action.Value.Name.Contains("UpdateAsync")) + .Select(action => action.Value) + .FirstOrDefault(); + + var tableContent = await _templateRenderer.RenderAsync( + "VbenTableView", + new + { + PagedRequest = pagedResultAction != null, + UpdatePermission = updateAction != null && updateAction.AllowAnonymous != null && updateAction.AllowAnonymous != true, + UpdatePermissionName = $"{moduleDefinition.RemoteServiceName.ToKebabCase()}.{controllerModel.ControllerName.ToKebabCase()}.Update", + DeletePermission = deleteAction != null && deleteAction.AllowAnonymous != null && deleteAction.AllowAnonymous != true, + DeletePermissionName = $"{moduleDefinition.RemoteServiceName.ToKebabCase()}.{controllerModel.ControllerName.ToKebabCase()}.Delete", + Application = controllerModel.ControllerName, + ModalName = $"{controllerModel.ControllerName.ToPascalCase()}Modal", + ApiPath = $"/@/api/{moduleDefinition.RemoteServiceName.ToKebabCase()}/{controllerModel.ControllerName.ToKebabCase()}", + RemoteService = moduleDefinition.RemoteServiceName, + }); + + return tableContent; + } + + public async virtual Task CreateIndex( + GenerateViewArgs args, + ApplicationApiDescriptionModel appModel, + ControllerApiDescriptionModel controllerModel) + { + var moduleDefinition = appModel.Modules + .Where(module => module.Key.Equals(args.Module)) + .Select(module => module.Value) + .FirstOrDefault(); + + var modalContent = await _templateRenderer.RenderAsync( + "VbenComponentIndex", + new + { + TableName = $"{controllerModel.ControllerName}Table", + Application = $"{moduleDefinition.RemoteServiceName.ToPascalCase()}{controllerModel.ControllerName.ToPascalCase()}".EnsureEndsWith('s') + }); + + return modalContent; + } +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/ViewGeneratorBase.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/ViewGeneratorBase.cs new file mode 100644 index 000000000..28edc7351 --- /dev/null +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/ViewGeneratorBase.cs @@ -0,0 +1,70 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Cli; +using Volo.Abp.Cli.Http; +using Volo.Abp.Cli.ServiceProxying; +using Volo.Abp.Http.Modeling; +using Volo.Abp.Json; + +namespace LINGYUN.Abp.Cli.UI; +public abstract class ViewGeneratorBase : IViewGenerator where T : IViewGenerator +{ + public IJsonSerializer JsonSerializer { get; } + + public CliHttpClientFactory CliHttpClientFactory { get; } + + public ILogger Logger { get; set; } + + protected ViewGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer) + { + CliHttpClientFactory = cliHttpClientFactory; + JsonSerializer = jsonSerializer; + Logger = NullLogger.Instance; + } + + protected virtual async Task GetApplicationApiDescriptionModelAsync(GenerateViewArgs args, ApplicationApiDescriptionModelRequestDto requestDto = null) + { + Check.NotNull(args.Url, nameof(args.Url)); + + var client = CliHttpClientFactory.CreateClient(); + + var apiDefinitionResult = await client.GetStringAsync(CliUrls.GetApiDefinitionUrl(args.Url, requestDto)); + var apiDefinition = JsonSerializer.Deserialize(apiDefinitionResult); + + var moduleDefinition = apiDefinition.Modules.FirstOrDefault(x => string.Equals(x.Key, args.Module, StringComparison.CurrentCultureIgnoreCase)).Value; + if (moduleDefinition == null) + { + throw new CliUsageException($"Module name: {args.Module} is invalid"); + } + + var serviceType = GetServiceType(args); + switch (serviceType) + { + case ServiceType.Application: + moduleDefinition.Controllers.RemoveAll(x => !x.Value.IsRemoteService); + break; + case ServiceType.Integration: + moduleDefinition.Controllers.RemoveAll(x => !x.Value.IsIntegrationService); + break; + } + + var apiDescriptionModel = ApplicationApiDescriptionModel.Create(); + apiDescriptionModel.Types = apiDefinition.Types; + apiDescriptionModel.AddModule(moduleDefinition); + return apiDescriptionModel; + } + + protected virtual ServiceType? GetServiceType(GenerateViewArgs args) + { + return args.ServiceType ?? GetDefaultServiceType(args); + } + + protected abstract ServiceType? GetDefaultServiceType(GenerateViewArgs args); + + public abstract Task GenerateAsync(GenerateViewArgs args); +} diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/Properties/launchSettings.json b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/Properties/launchSettings.json index a58b5a7c4..ad7255939 100644 --- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/Properties/launchSettings.json +++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/Properties/launchSettings.json @@ -2,7 +2,8 @@ "profiles": { "LINGYUN.Abp.Cli": { "commandName": "Project", - "commandLineArgs": "generate-proxy -t ts -asp uni-app-axios -u http://127.0.0.1:30025 -m Platform -o D:\\Projects\\Development\\type-script" + "commandLineArgs": "generate-view -t vben-view -m identity -o D:\\Projects\\Development\\view-script -url http://10.21.15.28:30015/" + //"commandLineArgs": "generate-proxy -t ts -asp uni-app-axios -u http://127.0.0.1:30025 -m Platform -o D:\\Projects\\Development\\type-script" } } } \ No newline at end of file