Release 1.1.0: QR-Reportimport, Regal-Auswahlmodal, Einlagern/Auslagern-Verknuepfungen aus Reifenlager, Stabilitaetsfixes
This commit is contained in:
1 parent
cba9414e92
commit
3404fb9875
25 files changed
+5106
-744
No files matched your search
@@ -0,0 +1,161 @@
|
||||
@implements IAsyncDisposable
|
||||
@inject IJSRuntime JS
|
||||
|
||||
@if (IsOpen)
|
||||
{
|
||||
<div class="qr-overlay" @onclick="Cancel">
|
||||
<div class="qr-dialog" role="dialog" aria-modal="true" aria-labelledby="qr-title" @onclick:stopPropagation="true">
|
||||
<div class="qr-header">
|
||||
<div>
|
||||
<h3 id="qr-title">@Title</h3>
|
||||
@if (!string.IsNullOrEmpty(Subtitle))
|
||||
{
|
||||
<p class="qr-subtitle">@Subtitle</p>
|
||||
}
|
||||
</div>
|
||||
<button type="button" class="qr-close" @onclick="Cancel" aria-label="Schließen">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="qr-body">
|
||||
<div id="@_elementId" class="qr-viewport"></div>
|
||||
|
||||
@if (isStarting)
|
||||
{
|
||||
<div class="qr-status"><span class="spinner"></span> Kamera wird gestartet…</div>
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(cameraError))
|
||||
{
|
||||
<div class="qr-status error">📷 @cameraError</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="qr-status">QR-Code des Reports vor die Kamera halten</div>
|
||||
}
|
||||
|
||||
<div class="qr-manual">
|
||||
<label for="qr-manual-input">Oder Link / Nummer einfügen</label>
|
||||
<div class="qr-manual-row">
|
||||
<input id="qr-manual-input" type="text" class="form-control" placeholder="https://files.cnlaunch.com/…?dataRecordSn=…"
|
||||
@bind="manualText" @bind:event="oninput" @onkeydown="OnManualKeyDown" />
|
||||
<button type="button" class="btn btn-primary" @onclick="SubmitManual" disabled="@string.IsNullOrWhiteSpace(manualText)">Übernehmen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public bool IsOpen { get; set; }
|
||||
[Parameter] public EventCallback<bool> IsOpenChanged { get; set; }
|
||||
[Parameter] public string Title { get; set; } = "QR-Code scannen";
|
||||
[Parameter] public string? Subtitle { get; set; }
|
||||
[Parameter] public EventCallback<string> OnScanned { get; set; }
|
||||
|
||||
private readonly string _elementId = $"qr-reader-{Guid.NewGuid():N}";
|
||||
private DotNetObjectReference<QrScannerModal>? _selfRef;
|
||||
private bool _scannerActive;
|
||||
private bool _wasOpen;
|
||||
private bool isStarting;
|
||||
private string? cameraError;
|
||||
private string manualText = "";
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (IsOpen && !_wasOpen)
|
||||
{
|
||||
_wasOpen = true;
|
||||
await StartScanner();
|
||||
}
|
||||
else if (!IsOpen && _wasOpen)
|
||||
{
|
||||
_wasOpen = false;
|
||||
await StopScanner();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartScanner()
|
||||
{
|
||||
cameraError = null;
|
||||
isStarting = true;
|
||||
manualText = "";
|
||||
StateHasChanged();
|
||||
|
||||
try
|
||||
{
|
||||
_selfRef ??= DotNetObjectReference.Create(this);
|
||||
await JS.InvokeVoidAsync("qrScanner.start", _elementId, _selfRef);
|
||||
_scannerActive = true;
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
cameraError = ExtractMessage(ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
cameraError = ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
isStarting = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExtractMessage(string raw)
|
||||
{
|
||||
if (raw.Contains("NotAllowedError", StringComparison.OrdinalIgnoreCase) || raw.Contains("Permission", StringComparison.OrdinalIgnoreCase))
|
||||
return "Kamerazugriff wurde verweigert. Bitte Berechtigung im Browser erlauben.";
|
||||
if (raw.Contains("NotFoundError", StringComparison.OrdinalIgnoreCase) || raw.Contains("Keine Kamera", StringComparison.OrdinalIgnoreCase))
|
||||
return "Keine Kamera gefunden.";
|
||||
var idx = raw.IndexOf('\n');
|
||||
return idx > 0 ? raw[..idx] : raw;
|
||||
}
|
||||
|
||||
private async Task StopScanner()
|
||||
{
|
||||
if (!_scannerActive) return;
|
||||
_scannerActive = false;
|
||||
try { await JS.InvokeVoidAsync("qrScanner.stop"); } catch { }
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public async Task OnQrScanned(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return;
|
||||
await Close();
|
||||
await OnScanned.InvokeAsync(text.Trim());
|
||||
}
|
||||
|
||||
private async Task SubmitManual()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(manualText)) return;
|
||||
var text = manualText.Trim();
|
||||
await Close();
|
||||
await OnScanned.InvokeAsync(text);
|
||||
}
|
||||
|
||||
private async Task OnManualKeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Enter") await SubmitManual();
|
||||
}
|
||||
|
||||
private async Task Cancel() => await Close();
|
||||
|
||||
private async Task Close()
|
||||
{
|
||||
await StopScanner();
|
||||
_wasOpen = false;
|
||||
if (IsOpen)
|
||||
{
|
||||
IsOpen = false;
|
||||
await IsOpenChanged.InvokeAsync(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await StopScanner();
|
||||
_selfRef?.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user