diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/AbpElsaNextStudioIdentityBlazorModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/AbpElsaNextStudioIdentityBlazorModule.cs
new file mode 100644
index 000000000..238bdd4dc
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/AbpElsaNextStudioIdentityBlazorModule.cs
@@ -0,0 +1,17 @@
+using LINGYUN.Abp.ElsaNext.Studio.Blazor;
+using LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.Extensions;
+using Volo.Abp.Identity;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor;
+
+[DependsOn(
+ typeof(AbpIdentityDomainModule),
+ typeof(AbpElsaNextStudioBlazorModule))]
+public class AbpElsaNextStudioIdentityBlazorModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddIdentityUIHintHandlers();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserPicker.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserPicker.razor
new file mode 100644
index 000000000..c876a1083
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserPicker.razor
@@ -0,0 +1,128 @@
+@using Elsa.Api.Client.Shared.UIHints.DropDown
+@using Elsa.Studio.Models
+@using LINGYUN.Abp.ElsaNext.Studio.Blazor.Components
+@using Volo.Abp.Identity
+@inject ILocalizer Localizer
+
+@inherits StudioComponentBase
+@{
+ var inputDescriptor = EditorContext.InputDescriptor;
+ var displayName = inputDescriptor.DisplayName;
+ var description = inputDescriptor.Description;
+}
+
+
+
+
+
+
+
+@code {
+ private SelectListItem? _selectedItem;
+
+ [Parameter] public DisplayInputEditorContext EditorContext { get; set; } = null!;
+
+ [Inject] protected IIdentityUserRepository IdentityUserRepository { get; set; } = null!;
+ [Inject] protected IDialogService DialogService { get; set; } = null!;
+
+ protected override async Task OnInitializedAsync()
+ {
+ var selectedItem = await ResolveSelectedItemAsync();
+
+ if (selectedItem != null)
+ {
+ await InvokeAsync(() => _selectedItem = selectedItem);
+ }
+ }
+
+ private async Task OpenSelectDialogAsync()
+ {
+ if (EditorContext.IsReadOnly)
+ {
+ return;
+ }
+
+ var inputDescriptor = EditorContext.InputDescriptor;
+ var parameters = new DialogParameters
+ {
+ { x => x.Title, Localizer[inputDescriptor.DisplayName ?? inputDescriptor.Name] },
+ { x => x.SelectedValue, _selectedItem?.Value }
+ };
+ var options = new DialogOptions
+ {
+ CloseOnEscapeKey = true,
+ FullWidth = true,
+ MaxWidth = MaxWidth.Medium
+ };
+
+ var dialog = await DialogService.ShowAsync(string.Empty, parameters, options);
+ var result = await dialog.Result;
+
+ if (result is { Canceled: false, Data: SelectListItem item })
+ {
+ await InvokeAsync(async () =>
+ {
+ _selectedItem = item;
+ await EditorContext.UpdateValueOrLiteralExpressionAsync(item.Value);
+ });
+ }
+ }
+
+ private MudTextFieldExIcon[] GetAdornmentIcons() =>
+ [
+ new()
+ {
+ Icon = Icons.Material.Filled.Search,
+ Tooltip = Localizer["Search"],
+ Disabled = EditorContext.IsReadOnly,
+ OnClick = OpenSelectDialogAsync
+ },
+ new()
+ {
+ Icon = Icons.Material.Filled.Clear,
+ Tooltip = Localizer["Clear"],
+ Visible = _selectedItem != null,
+ Disabled = EditorContext.IsReadOnly,
+ OnClick = ClearAsync
+ }
+ ];
+
+ private async Task ClearAsync()
+ {
+ await InvokeAsync(async () =>
+ {
+ _selectedItem = null;
+ await EditorContext.UpdateValueOrLiteralExpressionAsync(string.Empty);
+ });
+ }
+
+ private async Task ResolveSelectedItemAsync()
+ {
+ var value = EditorContext.GetLiteralValueOrDefault();
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return null;
+ }
+
+ if (Guid.TryParse(value, out var userId))
+ {
+ var user = await IdentityUserRepository.FindAsync(userId);
+
+ if (user != null)
+ {
+ return new SelectListItem(user.Name ?? user.UserName, user.Id.ToString());
+ }
+ }
+
+ return new SelectListItem(value, value);
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserSelectDialog.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserSelectDialog.razor
new file mode 100644
index 000000000..2eee7e131
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserSelectDialog.razor
@@ -0,0 +1,152 @@
+@using Elsa.Api.Client.Shared.UIHints.DropDown
+@using System.Threading
+@using Volo.Abp.Identity
+@inject ILocalizer Localizer
+
+@implements IDisposable
+
+
+
+ @Title
+
+
+
+
+
+
+
+ @Localizer["Name"]
+
+
+
+ @context.Text
+
+ @if (context.Value == SelectedValue)
+ {
+
+ }
+ else
+ {
+
+ @Localizer["Select"]
+
+ }
+
+
+
+ @Localizer["No results found"]
+
+
+
+
+
+ @Localizer["Cancel"]
+
+
+
+@code {
+ private const int DebounceIntervalMilliseconds = 350;
+
+ private MudTable? _table;
+ private string? _searchText;
+ private CancellationTokenSource? _debounceCancellationTokenSource;
+
+ [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ [Parameter] public string Title { get; set; } = string.Empty;
+
+ [Parameter] public string? SelectedValue { get; set; }
+
+ [Inject] protected IIdentityUserRepository IdentityUserRepository { get; set; } = null!;
+
+ public void Dispose()
+ {
+ _debounceCancellationTokenSource?.Cancel();
+ _debounceCancellationTokenSource?.Dispose();
+ }
+
+ private async Task OnSearchTextChangedAsync(string? searchValue)
+ {
+ _searchText = searchValue;
+
+ _debounceCancellationTokenSource?.Cancel();
+ _debounceCancellationTokenSource?.Dispose();
+ _debounceCancellationTokenSource = new CancellationTokenSource();
+
+ try
+ {
+ await Task.Delay(DebounceIntervalMilliseconds, _debounceCancellationTokenSource.Token);
+
+ var table = _table;
+
+ if (table != null)
+ {
+ await InvokeAsync(() => table.ReloadServerData());
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ }
+
+ private async Task> LoadUsersAsync(TableState state, CancellationToken cancellationToken)
+ {
+ var filter = string.IsNullOrWhiteSpace(_searchText) ? null : _searchText.Trim();
+
+ var users = await IdentityUserRepository.GetListAsync(
+ sorting: null,
+ maxResultCount: state.PageSize,
+ skipCount: state.Page * state.PageSize,
+ filter: filter,
+ cancellationToken: cancellationToken);
+
+ var totalCount = await IdentityUserRepository.GetCountAsync(
+ filter: filter,
+ cancellationToken: cancellationToken);
+
+ var items = users
+ .Select(user => new SelectListItem(user.Name ?? user.UserName, user.Id.ToString()))
+ .ToList();
+
+ return new TableData
+ {
+ Items = items,
+ TotalItems = (int)totalCount
+ };
+ }
+
+ private async Task OnRowClickAsync(TableRowClickEventArgs args)
+ {
+ await SelectAsync(args.Item);
+ }
+
+ private Task SelectAsync(SelectListItem? item)
+ {
+ if (item == null)
+ {
+ return Task.CompletedTask;
+ }
+
+ MudDialog.Close(DialogResult.Ok(item));
+ return Task.CompletedTask;
+ }
+
+ private void CancelAsync()
+ {
+ MudDialog.Cancel();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Extensions/ServiceCollectionExtensions.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 000000000..ec079e1b0
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,13 @@
+using Elsa.Studio.Extensions;
+using LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.Handlers;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.Extensions;
+
+public static class ServiceCollectionExtensions
+{
+ public static IServiceCollection AddIdentityUIHintHandlers(this IServiceCollection services)
+ {
+ return services.AddUIHintHandler();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xml b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xml
new file mode 100644
index 000000000..5d6962159
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xsd b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Handlers/IdentityPickerHandler.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Handlers/IdentityPickerHandler.cs
new file mode 100644
index 000000000..c02377f42
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Handlers/IdentityPickerHandler.cs
@@ -0,0 +1,25 @@
+using Elsa.Studio;
+using Elsa.Studio.Contracts;
+using Elsa.Studio.Models;
+using LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.Components;
+using LINGYUN.Abp.ElsaNext.UIHints;
+using Microsoft.AspNetCore.Components;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.Handlers;
+
+public class IdentityPickerHandler : IUIHintHandler
+{
+ public bool GetSupportsUIHint(string uiHint) => uiHint is AbpElsaUIHints.UserPicker;
+
+ public string UISyntax => WellKnownSyntaxNames.Object;
+
+ public RenderFragment DisplayInputEditor(DisplayInputEditorContext context)
+ {
+ return builder =>
+ {
+ builder.OpenComponent(0, typeof(IdentityUserPicker));
+ builder.AddAttribute(1, nameof(IdentityUserPicker.EditorContext), context);
+ builder.CloseComponent();
+ };
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.csproj b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.csproj
new file mode 100644
index 000000000..5457ddac3
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ net10.0
+ LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor
+ LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/_Imports.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/_Imports.razor
new file mode 100644
index 000000000..1f6d130e9
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/_Imports.razor
@@ -0,0 +1,5 @@
+@using Microsoft.AspNetCore.Components.Web
+@using Elsa.Studio.Components
+@using Elsa.Studio.Localization
+@using MudBlazor
+@using MudExtensions
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/AbpElsaNextStudioSaasBlazorModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/AbpElsaNextStudioSaasBlazorModule.cs
new file mode 100644
index 000000000..b0a02a7ae
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/AbpElsaNextStudioSaasBlazorModule.cs
@@ -0,0 +1,17 @@
+using LINGYUN.Abp.ElsaNext.Studio.Blazor;
+using LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.Extensions;
+using LINGYUN.Abp.Saas;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor;
+
+[DependsOn(
+ typeof(AbpSaasDomainModule),
+ typeof(AbpElsaNextStudioBlazorModule))]
+public class AbpElsaNextStudioSaasBlazorModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddSaasUIHintHandlers();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantPicker.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantPicker.razor
new file mode 100644
index 000000000..7fe21fe08
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantPicker.razor
@@ -0,0 +1,128 @@
+@using Elsa.Api.Client.Shared.UIHints.DropDown
+@using Elsa.Studio.Models
+@using LINGYUN.Abp.ElsaNext.Studio.Blazor.Components
+@using LINGYUN.Abp.Saas.Tenants
+@inject ILocalizer Localizer
+
+@inherits StudioComponentBase
+@{
+ var inputDescriptor = EditorContext.InputDescriptor;
+ var displayName = inputDescriptor.DisplayName;
+ var description = inputDescriptor.Description;
+}
+
+
+
+
+
+
+
+@code {
+ private SelectListItem? _selectedItem;
+
+ [Parameter] public DisplayInputEditorContext EditorContext { get; set; } = null!;
+
+ [Inject] protected ITenantRepository TenantRepository { get; set; } = null!;
+ [Inject] protected IDialogService DialogService { get; set; } = null!;
+
+ protected override async Task OnInitializedAsync()
+ {
+ var selectedItem = await ResolveSelectedItemAsync();
+
+ if (selectedItem != null)
+ {
+ await InvokeAsync(() => _selectedItem = selectedItem);
+ }
+ }
+
+ private async Task OpenSelectDialogAsync()
+ {
+ if (EditorContext.IsReadOnly)
+ {
+ return;
+ }
+
+ var inputDescriptor = EditorContext.InputDescriptor;
+ var parameters = new DialogParameters
+ {
+ { x => x.Title, Localizer[inputDescriptor.DisplayName ?? inputDescriptor.Name] },
+ { x => x.SelectedValue, _selectedItem?.Value }
+ };
+ var options = new DialogOptions
+ {
+ CloseOnEscapeKey = true,
+ FullWidth = true,
+ MaxWidth = MaxWidth.Medium
+ };
+
+ var dialog = await DialogService.ShowAsync(string.Empty, parameters, options);
+ var result = await dialog.Result;
+
+ if (result is { Canceled: false, Data: SelectListItem item })
+ {
+ await InvokeAsync(async () =>
+ {
+ _selectedItem = item;
+ await EditorContext.UpdateValueOrLiteralExpressionAsync(item.Value);
+ });
+ }
+ }
+
+ private MudTextFieldExIcon[] GetAdornmentIcons() =>
+ [
+ new()
+ {
+ Icon = Icons.Material.Filled.Search,
+ Tooltip = Localizer["Search"],
+ Disabled = EditorContext.IsReadOnly,
+ OnClick = OpenSelectDialogAsync
+ },
+ new()
+ {
+ Icon = Icons.Material.Filled.Clear,
+ Tooltip = Localizer["Clear"],
+ Visible = _selectedItem != null,
+ Disabled = EditorContext.IsReadOnly,
+ OnClick = ClearAsync
+ }
+ ];
+
+ private async Task ClearAsync()
+ {
+ await InvokeAsync(async () =>
+ {
+ _selectedItem = null;
+ await EditorContext.UpdateValueOrLiteralExpressionAsync(string.Empty);
+ });
+ }
+
+ private async Task ResolveSelectedItemAsync()
+ {
+ var value = EditorContext.GetLiteralValueOrDefault();
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return null;
+ }
+
+ if (Guid.TryParse(value, out var tenantId))
+ {
+ var tenant = await TenantRepository.FindAsync(tenantId);
+
+ if (tenant != null)
+ {
+ return new SelectListItem(tenant.Name, tenant.Id.ToString());
+ }
+ }
+
+ return new SelectListItem(value, value);
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantSelectDialog.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantSelectDialog.razor
new file mode 100644
index 000000000..253f7040f
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantSelectDialog.razor
@@ -0,0 +1,151 @@
+@using Elsa.Api.Client.Shared.UIHints.DropDown
+@using System.Threading
+@using LINGYUN.Abp.Saas.Tenants
+@inject ILocalizer Localizer
+
+@implements IDisposable
+
+
+
+ @Title
+
+
+
+
+
+
+
+ @Localizer["Name"]
+
+
+
+ @context.Text
+
+ @if (context.Value == SelectedValue)
+ {
+
+ }
+ else
+ {
+
+ @Localizer["Select"]
+
+ }
+
+
+
+ @Localizer["No results found"]
+
+
+
+
+
+ @Localizer["Cancel"]
+
+
+
+@code {
+ private const int DebounceIntervalMilliseconds = 350;
+
+ private MudTable? _table;
+ private string? _searchText;
+ private CancellationTokenSource? _debounceCancellationTokenSource;
+
+ [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ [Parameter] public string Title { get; set; } = string.Empty;
+
+ [Parameter] public string? SelectedValue { get; set; }
+
+ [Inject] protected ITenantRepository TenantRepository { get; set; } = null!;
+
+ public void Dispose()
+ {
+ _debounceCancellationTokenSource?.Cancel();
+ _debounceCancellationTokenSource?.Dispose();
+ }
+
+ private async Task OnSearchTextChangedAsync(string? searchValue)
+ {
+ _searchText = searchValue;
+
+ _debounceCancellationTokenSource?.Cancel();
+ _debounceCancellationTokenSource?.Dispose();
+ _debounceCancellationTokenSource = new CancellationTokenSource();
+
+ try
+ {
+ await Task.Delay(DebounceIntervalMilliseconds, _debounceCancellationTokenSource.Token);
+
+ var table = _table;
+
+ if (table != null)
+ {
+ await InvokeAsync(() => table.ReloadServerData());
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ }
+
+ private async Task> LoadTenantsAsync(TableState state, CancellationToken cancellationToken)
+ {
+ var filter = string.IsNullOrWhiteSpace(_searchText) ? null : _searchText.Trim();
+
+ var tenants = await TenantRepository.GetListAsync(
+ filter: filter,
+ maxResultCount: state.PageSize,
+ skipCount: state.Page * state.PageSize,
+ cancellationToken: cancellationToken);
+
+ var totalCount = await TenantRepository.GetCountAsync(
+ filter: filter,
+ cancellationToken: cancellationToken);
+
+ var items = tenants
+ .Select(tenant => new SelectListItem(tenant.Name, tenant.Id.ToString()))
+ .ToList();
+
+ return new TableData
+ {
+ Items = items,
+ TotalItems = (int)totalCount
+ };
+ }
+
+ private async Task OnRowClickAsync(TableRowClickEventArgs args)
+ {
+ await SelectAsync(args.Item);
+ }
+
+ private Task SelectAsync(SelectListItem? item)
+ {
+ if (item == null)
+ {
+ return Task.CompletedTask;
+ }
+
+ MudDialog.Close(DialogResult.Ok(item));
+ return Task.CompletedTask;
+ }
+
+ private void CancelAsync()
+ {
+ MudDialog.Cancel();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Extensions/ServiceCollectionExtensions.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 000000000..18a820d87
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,13 @@
+using Elsa.Studio.Extensions;
+using LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.Handlers;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.Extensions;
+
+public static class ServiceCollectionExtensions
+{
+ public static IServiceCollection AddSaasUIHintHandlers(this IServiceCollection services)
+ {
+ return services.AddUIHintHandler();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xml b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xml
new file mode 100644
index 000000000..5d6962159
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xsd b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Handlers/TenantPickerHandler.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Handlers/TenantPickerHandler.cs
new file mode 100644
index 000000000..e3859ec2d
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Handlers/TenantPickerHandler.cs
@@ -0,0 +1,25 @@
+using Elsa.Studio;
+using Elsa.Studio.Contracts;
+using Elsa.Studio.Models;
+using LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.Components;
+using LINGYUN.Abp.ElsaNext.UIHints;
+using Microsoft.AspNetCore.Components;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.Handlers;
+
+public class TenantPickerHandler : IUIHintHandler
+{
+ public bool GetSupportsUIHint(string uiHint) => uiHint is AbpElsaUIHints.TenantPicker;
+
+ public string UISyntax => WellKnownSyntaxNames.Object;
+
+ public RenderFragment DisplayInputEditor(DisplayInputEditorContext context)
+ {
+ return builder =>
+ {
+ builder.OpenComponent(0, typeof(TenantPicker));
+ builder.AddAttribute(1, nameof(TenantPicker.EditorContext), context);
+ builder.CloseComponent();
+ };
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.csproj b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.csproj
new file mode 100644
index 000000000..e973fa786
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ net10.0
+ LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor
+ LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor
+ false
+ false
+ false
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/_Imports.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/_Imports.razor
new file mode 100644
index 000000000..1f6d130e9
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/_Imports.razor
@@ -0,0 +1,5 @@
+@using Microsoft.AspNetCore.Components.Web
+@using Elsa.Studio.Components
+@using Elsa.Studio.Localization
+@using MudBlazor
+@using MudExtensions
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext/LINGYUN/Abp/ElsaNext/UIHints/AbpElsaUIHints.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext/LINGYUN/Abp/ElsaNext/UIHints/AbpElsaUIHints.cs
new file mode 100644
index 000000000..34004c52e
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext/LINGYUN/Abp/ElsaNext/UIHints/AbpElsaUIHints.cs
@@ -0,0 +1,7 @@
+namespace LINGYUN.Abp.ElsaNext.UIHints;
+
+public static class AbpElsaUIHints
+{
+ public const string TenantPicker = "tenant-picker";
+ public const string UserPicker = "user-picker";
+}