更新最新版20260604
This commit is contained in:
@@ -21,9 +21,11 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
|||||||
private const string DataSheetName = "实时数据";
|
private const string DataSheetName = "实时数据";
|
||||||
private const int StandardTrialCount = 3;
|
private const int StandardTrialCount = 3;
|
||||||
|
|
||||||
public string Export(SlipReportExport report)
|
public string Export(SlipReportExport report, string? exportDirectory = null)
|
||||||
{
|
{
|
||||||
var directory = GetExportDirectory();
|
var directory = string.IsNullOrWhiteSpace(exportDirectory)
|
||||||
|
? GetDefaultExportDirectory()
|
||||||
|
: exportDirectory;
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
var safeNumber = MakeSafeFileName(string.IsNullOrWhiteSpace(report.TestNumber)
|
var safeNumber = MakeSafeFileName(string.IsNullOrWhiteSpace(report.TestNumber)
|
||||||
@@ -52,7 +54,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetExportDirectory()
|
public static string GetDefaultExportDirectory()
|
||||||
{
|
{
|
||||||
var legacyDirectory = Path.Combine(@"D:\文件保存", DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture));
|
var legacyDirectory = Path.Combine(@"D:\文件保存", DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture));
|
||||||
if (Directory.Exists(@"D:\"))
|
if (Directory.Exists(@"D:\"))
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
@@ -32,6 +35,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
private const string DefaultAdcPortName = "COM4";
|
private const string DefaultAdcPortName = "COM4";
|
||||||
private const string LegacyPlcPortName = "COM7";
|
private const string LegacyPlcPortName = "COM7";
|
||||||
private const string LegacyAdcPortName = "COM8";
|
private const string LegacyAdcPortName = "COM8";
|
||||||
|
private static readonly TimeSpan ResetButtonPendingTimeout = TimeSpan.FromMilliseconds(800);
|
||||||
|
|
||||||
private readonly SlipResistanceDeviceService deviceService = new();
|
private readonly SlipResistanceDeviceService deviceService = new();
|
||||||
private readonly SlipExcelExportService excelExportService = new();
|
private readonly SlipExcelExportService excelExportService = new();
|
||||||
@@ -45,6 +49,9 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
|
|
||||||
private bool isLoadingDeviceSettings;
|
private bool isLoadingDeviceSettings;
|
||||||
private bool wasRunning;
|
private bool wasRunning;
|
||||||
|
private bool isResetButtonPending;
|
||||||
|
private bool hasObservedResetDeviceBusy;
|
||||||
|
private DateTime resetButtonPendingStartedAt = DateTime.MinValue;
|
||||||
private string activePlcPortName = string.Empty;
|
private string activePlcPortName = string.Empty;
|
||||||
private string activeAdcPortName = string.Empty;
|
private string activeAdcPortName = string.Empty;
|
||||||
private int activeBaudRate;
|
private int activeBaudRate;
|
||||||
@@ -258,7 +265,20 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
private async Task Clear()
|
private async Task Clear()
|
||||||
{
|
{
|
||||||
ApplyConnectionSettings();
|
ApplyConnectionSettings();
|
||||||
await RunDeviceCommand(deviceService.PulseResetAsync(), "已按老代码逻辑发送复位指令 M90");
|
BeginResetButtonFeedback();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await deviceService.PulseResetAsync();
|
||||||
|
CurrentStatus = "已按老代码逻辑发送复位指令 M90";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "设备指令失败:已按老代码逻辑发送复位指令 M90");
|
||||||
|
CurrentStatus = $"设备指令失败:{ex.Message}";
|
||||||
|
ClearResetButtonFeedback();
|
||||||
|
UpdateResetButtonText(deviceService.CurrentSnapshot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
@@ -293,7 +313,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void ExportReport()
|
private async Task ExportReport()
|
||||||
{
|
{
|
||||||
var points = currentRun.Count > 0 ? currentRun.ToList() : lastCompletedRun;
|
var points = currentRun.Count > 0 ? currentRun.ToList() : lastCompletedRun;
|
||||||
if (points.Count == 0)
|
if (points.Count == 0)
|
||||||
@@ -305,6 +325,15 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
CurrentStatus = "请选择 Excel 报告保存目录";
|
||||||
|
var exportDirectory = await SelectExportDirectoryAsync();
|
||||||
|
if (exportDirectory is null)
|
||||||
|
{
|
||||||
|
Log.Information("用户取消 Excel 导出目录选择:TestNumber={TestNumber}, PointCount={PointCount}", TestNumber, points.Count);
|
||||||
|
CurrentStatus = "已取消 Excel 导出";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var report = new SlipReportExport(
|
var report = new SlipReportExport(
|
||||||
TestNumber,
|
TestNumber,
|
||||||
OperatorName,
|
OperatorName,
|
||||||
@@ -322,7 +351,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
Samples.ToList(),
|
Samples.ToList(),
|
||||||
points);
|
points);
|
||||||
|
|
||||||
var path = excelExportService.Export(report);
|
var path = excelExportService.Export(report, exportDirectory);
|
||||||
UploadProgress = 100;
|
UploadProgress = 100;
|
||||||
CurrentStatus = $"Excel 已导出:{path}";
|
CurrentStatus = $"Excel 已导出:{path}";
|
||||||
}
|
}
|
||||||
@@ -333,6 +362,79 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string?> SelectExportDirectoryAsync()
|
||||||
|
{
|
||||||
|
var storageProvider = GetStorageProvider();
|
||||||
|
if (storageProvider is null || !storageProvider.CanPickFolder)
|
||||||
|
{
|
||||||
|
var defaultDirectory = SlipExcelExportService.GetDefaultExportDirectory();
|
||||||
|
Log.Warning("当前平台无法打开 Excel 导出目录选择器,将使用默认目录:Path={Path}", defaultDirectory);
|
||||||
|
return defaultDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
var folders = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = "选择报告保存目录",
|
||||||
|
AllowMultiple = false,
|
||||||
|
SuggestedStartLocation = await TryGetSuggestedExportFolderAsync(storageProvider)
|
||||||
|
});
|
||||||
|
|
||||||
|
var selectedFolder = folders.FirstOrDefault();
|
||||||
|
if (selectedFolder is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var localPath = selectedFolder.TryGetLocalPath();
|
||||||
|
if (string.IsNullOrWhiteSpace(localPath) && selectedFolder.Path.IsFile)
|
||||||
|
{
|
||||||
|
localPath = selectedFolder.Path.LocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(localPath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("选择的目录不是本地文件夹,无法保存 Excel");
|
||||||
|
}
|
||||||
|
|
||||||
|
return localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IStorageFolder?> TryGetSuggestedExportFolderAsync(IStorageProvider storageProvider)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var defaultDirectory = SlipExcelExportService.GetDefaultExportDirectory();
|
||||||
|
if (Directory.Exists(defaultDirectory))
|
||||||
|
{
|
||||||
|
return await storageProvider.TryGetFolderFromPathAsync(defaultDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentDirectory = Path.GetDirectoryName(defaultDirectory);
|
||||||
|
if (!string.IsNullOrWhiteSpace(parentDirectory) && Directory.Exists(parentDirectory))
|
||||||
|
{
|
||||||
|
return await storageProvider.TryGetFolderFromPathAsync(parentDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "准备 Excel 默认导出目录失败,将不设置目录选择初始位置");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IStorageProvider? GetStorageProvider()
|
||||||
|
{
|
||||||
|
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop
|
||||||
|
&& desktop.MainWindow is not null)
|
||||||
|
{
|
||||||
|
return desktop.MainWindow.StorageProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private Task LiftMotion() => RunDeviceCommand(deviceService.ApplyOldLiftAsync(), "提升按老代码逻辑执行:M4=0, M5=1");
|
private Task LiftMotion() => RunDeviceCommand(deviceService.ApplyOldLiftAsync(), "提升按老代码逻辑执行:M4=0, M5=1");
|
||||||
|
|
||||||
@@ -478,7 +580,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
{
|
{
|
||||||
DeviceStatus = device.IsTestRunning ? "联机 / 测试中" : device.IsResetting ? "联机 / 复位中" : "联机 / 待机";
|
DeviceStatus = device.IsTestRunning ? "联机 / 测试中" : device.IsResetting ? "联机 / 复位中" : "联机 / 待机";
|
||||||
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
||||||
ResetButtonText = device.IsResetting ? "复位中" : "复位";
|
UpdateResetButtonText(device);
|
||||||
VerticalPressure = device.VerticalLoadN.ToString("F1", CultureInfo.InvariantCulture);
|
VerticalPressure = device.VerticalLoadN.ToString("F1", CultureInfo.InvariantCulture);
|
||||||
HorizontalForce = device.HorizontalFrictionN.ToString("F1", CultureInfo.InvariantCulture);
|
HorizontalForce = device.HorizontalFrictionN.ToString("F1", CultureInfo.InvariantCulture);
|
||||||
FrictionCoefficient = device.FrictionCoefficient.ToString("F3", CultureInfo.InvariantCulture);
|
FrictionCoefficient = device.FrictionCoefficient.ToString("F3", CultureInfo.InvariantCulture);
|
||||||
@@ -489,7 +591,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
{
|
{
|
||||||
DeviceStatus = IsAdcCalibrationError(device.LastError) ? "数据无效" : "离线";
|
DeviceStatus = IsAdcCalibrationError(device.LastError) ? "数据无效" : "离线";
|
||||||
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
||||||
ResetButtonText = device.IsResetting ? "复位中" : "复位";
|
UpdateResetButtonText(device);
|
||||||
VerticalPressure = "--";
|
VerticalPressure = "--";
|
||||||
HorizontalForce = "--";
|
HorizontalForce = "--";
|
||||||
FrictionCoefficient = "--";
|
FrictionCoefficient = "--";
|
||||||
@@ -530,6 +632,37 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
|||||||
wasRunning = isRecording;
|
wasRunning = isRecording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BeginResetButtonFeedback()
|
||||||
|
{
|
||||||
|
isResetButtonPending = true;
|
||||||
|
hasObservedResetDeviceBusy = false;
|
||||||
|
resetButtonPendingStartedAt = DateTime.UtcNow;
|
||||||
|
ResetButtonText = "复位中";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearResetButtonFeedback()
|
||||||
|
{
|
||||||
|
isResetButtonPending = false;
|
||||||
|
hasObservedResetDeviceBusy = false;
|
||||||
|
resetButtonPendingStartedAt = DateTime.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateResetButtonText(SlipDeviceSnapshot device)
|
||||||
|
{
|
||||||
|
if (device.IsResetting)
|
||||||
|
{
|
||||||
|
hasObservedResetDeviceBusy = true;
|
||||||
|
}
|
||||||
|
else if (!device.IsConnected
|
||||||
|
|| hasObservedResetDeviceBusy
|
||||||
|
|| (isResetButtonPending && DateTime.UtcNow - resetButtonPendingStartedAt >= ResetButtonPendingTimeout))
|
||||||
|
{
|
||||||
|
ClearResetButtonFeedback();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetButtonText = device.IsResetting || isResetButtonPending ? "复位中" : "复位";
|
||||||
|
}
|
||||||
|
|
||||||
private void BeginRun()
|
private void BeginRun()
|
||||||
{
|
{
|
||||||
currentRun.Clear();
|
currentRun.Clear();
|
||||||
|
|||||||
@@ -7,16 +7,14 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="1440"
|
d:DesignWidth="1024"
|
||||||
d:DesignHeight="900"
|
d:DesignHeight="768"
|
||||||
x:Class="Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Views.MainWindow"
|
x:Class="Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Views.MainWindow"
|
||||||
x:DataType="vm:MainWindowViewModel"
|
x:DataType="vm:MainWindowViewModel"
|
||||||
Icon="/Assets/avalonia-logo.ico"
|
Icon="/Assets/avalonia-logo.ico"
|
||||||
WindowState="Maximized"
|
WindowState="Maximized"
|
||||||
Width="1280"
|
Width="1024"
|
||||||
Height="800"
|
Height="768"
|
||||||
MinWidth="1024"
|
|
||||||
MinHeight="720"
|
|
||||||
Title="鞋类 整鞋试验方法 防滑性能测定-GB/T 3903.6-2024"
|
Title="鞋类 整鞋试验方法 防滑性能测定-GB/T 3903.6-2024"
|
||||||
Background="#F4F7FB"
|
Background="#F4F7FB"
|
||||||
BackgroundAnimationEnabled="False"
|
BackgroundAnimationEnabled="False"
|
||||||
@@ -195,11 +193,11 @@
|
|||||||
</suki:SukiWindow.Styles>
|
</suki:SukiWindow.Styles>
|
||||||
|
|
||||||
<Grid RowDefinitions="*"
|
<Grid RowDefinitions="*"
|
||||||
Width="1264"
|
MaxWidth="1264"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Stretch"
|
||||||
Margin="8"
|
Margin="8"
|
||||||
ClipToBounds="True">
|
ClipToBounds="True">
|
||||||
<Grid Grid.Row="0" RowDefinitions="Auto,Auto,*" RowSpacing="8">
|
<Grid Grid.Row="0" RowDefinitions="Auto,*" RowSpacing="8">
|
||||||
<Border Classes="panel">
|
<Border Classes="panel">
|
||||||
<Grid ColumnDefinitions="1.2*,1.15*" ColumnSpacing="14">
|
<Grid ColumnDefinitions="1.2*,1.15*" ColumnSpacing="14">
|
||||||
<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,*,Auto,*" RowSpacing="6" ColumnSpacing="8">
|
<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,*,Auto,*" RowSpacing="6" ColumnSpacing="8">
|
||||||
@@ -249,57 +247,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border Grid.Row="1"
|
<DockPanel Grid.Row="1" LastChildFill="True" MinHeight="0" ClipToBounds="True">
|
||||||
Background="{StaticResource PanelBrush}"
|
|
||||||
BorderBrush="{StaticResource LineBrush}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="8,4"
|
|
||||||
ClipToBounds="True">
|
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Disabled"
|
|
||||||
ClipToBounds="True">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
|
||||||
<TextBlock Text="标准数据" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="设备状态" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DeviceStatus}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="正压力(N)" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding VerticalPressure}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="摩擦力(N)" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding HorizontalForce}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="位移(mm)" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding Distance}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="实时系数" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding FrictionCoefficient}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="静摩擦系数" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding StaticCoefficient}" FontWeight="SemiBold" Foreground="#5B21B6" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="动摩擦系数" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DynamicCoefficient}" FontWeight="SemiBold" Foreground="#047857" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBlock Text="结果" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding ResultSummary}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<TextBlock Text="{Binding StandardReference}" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding CurrentStatus}" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<DockPanel Grid.Row="2" LastChildFill="True" MinHeight="0" ClipToBounds="True">
|
|
||||||
<Border DockPanel.Dock="Left" Width="280" Classes="panel" Margin="0,0,10,0">
|
<Border DockPanel.Dock="Left" Width="280" Classes="panel" Margin="0,0,10,0">
|
||||||
<Grid RowDefinitions="Auto,Auto,*" RowSpacing="8">
|
<Grid RowDefinitions="Auto,Auto,*" RowSpacing="8">
|
||||||
<TextBlock Text="实时数据"
|
<TextBlock Text="实时数据"
|
||||||
|
|||||||
Reference in New Issue
Block a user