Browse Source

feat: Add IdentityUserPicker/TenantPicker UIHinhHander

pull/1568/head
colin 1 week ago
parent
commit
a76001bea2
  1. 17
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/AbpElsaNextStudioIdentityBlazorModule.cs
  2. 128
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserPicker.razor
  3. 152
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Components/IdentityUserSelectDialog.razor
  4. 13
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Extensions/ServiceCollectionExtensions.cs
  5. 3
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xml
  6. 30
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xsd
  7. 25
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/Handlers/IdentityPickerHandler.cs
  8. 23
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.csproj
  9. 5
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/_Imports.razor
  10. 17
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/AbpElsaNextStudioSaasBlazorModule.cs
  11. 128
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantPicker.razor
  12. 151
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Components/TenantSelectDialog.razor
  13. 13
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Extensions/ServiceCollectionExtensions.cs
  14. 3
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xml
  15. 30
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xsd
  16. 25
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/Handlers/TenantPickerHandler.cs
  17. 20
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.csproj
  18. 5
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/_Imports.razor
  19. 7
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext/LINGYUN/Abp/ElsaNext/UIHints/AbpElsaUIHints.cs

17
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();
}
}

128
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;
}
<ExpressionInput EditorContext="@EditorContext">
<ChildContent>
<MudTextFieldEx Value="@(_selectedItem?.Text ?? string.Empty)"
Label="@Localizer[displayName]"
HelperText="@Localizer[description]"
Variant="Variant.Outlined"
Margin="Margin.Dense"
ReadOnly="true"
Disabled="EditorContext.IsReadOnly"
OnFieldClick="@(_ => OpenSelectDialogAsync())"
AdornmentIcons="GetAdornmentIcons()" />
</ChildContent>
</ExpressionInput>
@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<IdentityUserSelectDialog>
{
{ 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<IdentityUserSelectDialog>(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<SelectListItem?> 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);
}
}

152
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
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">@Title</MudText>
</TitleContent>
<DialogContent>
<MudStack Spacing="3">
<MudTextField T="string"
Value="_searchText"
ValueChanged="OnSearchTextChangedAsync"
Immediate="true"
Placeholder="@Localizer["Search"]"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
Variant="Variant.Outlined"
Margin="Margin.Dense" />
<MudTable T="SelectListItem"
@ref="_table"
ServerData="LoadUsersAsync"
Dense="true"
Hover="true"
Breakpoint="Breakpoint.None"
OnRowClick="OnRowClickAsync">
<HeaderContent>
<MudTh>@Localizer["Name"]</MudTh>
<MudTh Style="text-align:right" />
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="@Localizer["Name"]">@context.Text</MudTd>
<MudTd DataLabel="" Style="text-align:right">
@if (context.Value == SelectedValue)
{
<MudIcon Icon="@Icons.Material.Filled.Check" Color="Color.Success" Size="Size.Small" />
}
else
{
<MudButton Size="Size.Small" Variant="Variant.Text" Color="Color.Primary" OnClick="@(() => SelectAsync(context))">
@Localizer["Select"]
</MudButton>
}
</MudTd>
</RowTemplate>
<NoRecordsContent>
<MudText Typo="Typo.body2">@Localizer["No results found"]</MudText>
</NoRecordsContent>
</MudTable>
</MudStack>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Text" OnClick="CancelAsync">@Localizer["Cancel"]</MudButton>
</DialogActions>
</MudDialog>
@code {
private const int DebounceIntervalMilliseconds = 350;
private MudTable<SelectListItem>? _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<TableData<SelectListItem>> 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<SelectListItem>
{
Items = items,
TotalItems = (int)totalCount
};
}
private async Task OnRowClickAsync(TableRowClickEventArgs<SelectListItem> 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();
}
}

13
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<IdentityPickerHandler>();
}
}

3
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

25
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();
};
}
}

23
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor/LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor.csproj

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor</AssemblyName>
<PackageId>LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Identity.Domain" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.ElsaNext.Studio.Blazor\LINGYUN.Abp.ElsaNext.Studio.Blazor.csproj" />
</ItemGroup>
</Project>

5
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

17
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();
}
}

128
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;
}
<ExpressionInput EditorContext="@EditorContext">
<ChildContent>
<MudTextFieldEx Value="@(_selectedItem?.Text ?? string.Empty)"
Label="@Localizer[displayName]"
HelperText="@Localizer[description]"
Variant="Variant.Outlined"
Margin="Margin.Dense"
ReadOnly="true"
Disabled="EditorContext.IsReadOnly"
OnFieldClick="@(_ => OpenSelectDialogAsync())"
AdornmentIcons="GetAdornmentIcons()" />
</ChildContent>
</ExpressionInput>
@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<TenantSelectDialog>
{
{ 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<TenantSelectDialog>(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<SelectListItem?> 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);
}
}

151
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
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">@Title</MudText>
</TitleContent>
<DialogContent>
<MudStack Spacing="3">
<MudTextField T="string"
Value="_searchText"
ValueChanged="OnSearchTextChangedAsync"
Immediate="true"
Placeholder="@Localizer["Search"]"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
Variant="Variant.Outlined"
Margin="Margin.Dense" />
<MudTable T="SelectListItem"
@ref="_table"
ServerData="LoadTenantsAsync"
Dense="true"
Hover="true"
Breakpoint="Breakpoint.None"
OnRowClick="OnRowClickAsync">
<HeaderContent>
<MudTh>@Localizer["Name"]</MudTh>
<MudTh Style="text-align:right" />
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="@Localizer["Name"]">@context.Text</MudTd>
<MudTd DataLabel="" Style="text-align:right">
@if (context.Value == SelectedValue)
{
<MudIcon Icon="@Icons.Material.Filled.Check" Color="Color.Success" Size="Size.Small" />
}
else
{
<MudButton Size="Size.Small" Variant="Variant.Text" Color="Color.Primary" OnClick="@(() => SelectAsync(context))">
@Localizer["Select"]
</MudButton>
}
</MudTd>
</RowTemplate>
<NoRecordsContent>
<MudText Typo="Typo.body2">@Localizer["No results found"]</MudText>
</NoRecordsContent>
</MudTable>
</MudStack>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Text" OnClick="CancelAsync">@Localizer["Cancel"]</MudButton>
</DialogActions>
</MudDialog>
@code {
private const int DebounceIntervalMilliseconds = 350;
private MudTable<SelectListItem>? _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<TableData<SelectListItem>> 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<SelectListItem>
{
Items = items,
TotalItems = (int)totalCount
};
}
private async Task OnRowClickAsync(TableRowClickEventArgs<SelectListItem> 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();
}
}

13
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<TenantPickerHandler>();
}
}

3
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

25
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();
};
}
}

20
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor/LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor</AssemblyName>
<PackageId>LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\saas\LINGYUN.Abp.Saas.Domain\LINGYUN.Abp.Saas.Domain.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.ElsaNext.Studio.Blazor\LINGYUN.Abp.ElsaNext.Studio.Blazor.csproj" />
</ItemGroup>
</Project>

5
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

7
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";
}
Loading…
Cancel
Save