Bilder hinzufügen
This commit is contained in:
parent
fda4543775
commit
92e92142ce
@ -19,23 +19,20 @@
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<!-- === BESTEHENDE FELDER (unverändert) === -->
|
||||
<div class="row g-3">
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Bezeichnung</label>
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.Bezeichnung" placeholder="z. B. Filter Nordhalle" />
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.Bezeichnung" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Typ</label>
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.Typ" placeholder="z. B. Aktivkohle" />
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.Typ" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Seriennummer</label>
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.Seriennummer" placeholder="z. B. SN-2024-0012" />
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.Seriennummer" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Zustand</label>
|
||||
<InputSelect class="form-select form-select-lg" @bind-Value="filter.Zustand">
|
||||
@ -45,72 +42,75 @@
|
||||
<option>Defekt</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Einbaudatum</label>
|
||||
<InputDate class="form-control form-control-lg" @bind-Value="filter.Einbaudatum" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Letzte Wartung</label>
|
||||
<InputDate class="form-control form-control-lg" @bind-Value="filter.LetzteWartung" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">QR-Code</label>
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.QRCode" placeholder="z. B. FC-Q12345" />
|
||||
<InputText class="form-control form-control-lg" @bind-Value="filter.QRCode" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
<!-- === NEU: FOTO-BEREICH === -->
|
||||
<div class="mt-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0 text-primary">
|
||||
<i class="bi bi-camera me-2"></i> Fotos (@filter.Photos.Count)
|
||||
</h5>
|
||||
<button type="button" class="btn btn-outline-primary rounded-pill" @onclick="TakePhoto">
|
||||
<i class="bi bi-camera-fill me-1"></i> Foto aufnehmen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (filter.Photos.Any())
|
||||
{
|
||||
<div class="row g-3">
|
||||
@foreach (var photo in filter.Photos)
|
||||
{
|
||||
<div class="col-6 col-md-4 col-lg-3">
|
||||
<div class="position-relative">
|
||||
<img src="@photo" class="img-fluid rounded shadow-sm" style="height:180px; object-fit:cover; width:100%;" />
|
||||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 m-2 rounded-circle"
|
||||
@onclick="() => RemovePhoto(photo)">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-light text-center py-4">
|
||||
<i class="bi bi-image fs-1 text-muted"></i>
|
||||
<p class="mt-2 text-muted">Noch keine Fotos – tippe auf "Foto aufnehmen"</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-5">
|
||||
<button type="submit" class="btn btn-primary btn-lg rounded-pill shadow-sm">
|
||||
<i class="bi bi-save me-2"></i> Speichern
|
||||
</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
@if (showToast)
|
||||
{
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
<div class="toast align-items-center text-bg-success border-0 show fade-in-toast">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
✅ Filterdaten erfolgreich gespeichert!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private FilterModel filter = new();
|
||||
private bool showToast;
|
||||
|
||||
private async Task SaveForm()
|
||||
{
|
||||
bool success = await FilterService.SaveFilterAsync(filter);
|
||||
if (success)
|
||||
{
|
||||
await JS.InvokeVoidAsync("showToast", "✅ Offline gespeichert – wird synchronisiert!", "success");
|
||||
NavManager.NavigateTo($"/stations/{filter.CustomerId}/filters/{filter.StationId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await JS.InvokeVoidAsync("showToast", "Fehler", "error");
|
||||
}
|
||||
}
|
||||
|
||||
private FilterModel filter = new() { Photos = new List<string>() };
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
|
||||
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
|
||||
|
||||
// Standardwerte setzen
|
||||
if (int.TryParse(query.Get("customerId"), out int customerId))
|
||||
filter.CustomerId = customerId;
|
||||
|
||||
@ -123,11 +123,44 @@
|
||||
if (existing != null)
|
||||
{
|
||||
filter = existing;
|
||||
StateHasChanged();
|
||||
// Sicherstellen, dass Photos existieren
|
||||
filter.Photos ??= new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TakePhoto()
|
||||
{
|
||||
try
|
||||
{
|
||||
var photoBase64 = await JS.InvokeAsync<string>("takePhoto");
|
||||
if (!string.IsNullOrEmpty(photoBase64))
|
||||
{
|
||||
filter.Photos.Add(photoBase64);
|
||||
StateHasChanged();
|
||||
await JS.InvokeVoidAsync("showToast", "Foto hinzugefügt!", "success");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JS.InvokeVoidAsync("showToast", "Kamera-Fehler", "error");
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemovePhoto(string photo)
|
||||
{
|
||||
filter.Photos.Remove(photo);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
}
|
||||
private async Task SaveForm()
|
||||
{
|
||||
bool success = await FilterService.SaveFilterAsync(filter);
|
||||
if (success)
|
||||
{
|
||||
await JS.InvokeVoidAsync("showToast", "Filter + Fotos gespeichert!", "success");
|
||||
NavManager.NavigateTo($"/stations/{filter.CustomerId}/filters/{filter.StationId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,6 +47,7 @@
|
||||
<script src="https://unpkg.com/dexie@3.2.4/dist/dexie.min.js"></script>
|
||||
<script src="js/filterdb.js"></script>
|
||||
<script src="js/onlineStatus.js"></script>
|
||||
<script src="js/camera.js"></script>
|
||||
|
||||
<script src="_framework/blazor.webassembly.js"></script>
|
||||
<script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>
|
||||
|
||||
17
FilterCair.Client/wwwroot/js/camera.js
Normal file
17
FilterCair.Client/wwwroot/js/camera.js
Normal file
@ -0,0 +1,17 @@
|
||||
window.takePhoto = async () => {
|
||||
const video = document.createElement('video');
|
||||
const canvas = document.createElement('canvas');
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
|
||||
video.srcObject = stream;
|
||||
video.play();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
video.onloadedmetadata = () => {
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
canvas.getContext('2d').drawImage(video, 0, 0);
|
||||
stream.getTracks().forEach(t => t.stop());
|
||||
resolve(canvas.toDataURL('image/jpeg', 0.8));
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -19,5 +19,7 @@ namespace FilterCair.Shared.Models
|
||||
public DateTime? LetzteWartung { get; set; }
|
||||
public string Zustand { get; set; } = string.Empty;
|
||||
public string QRCode { get; set; } = string.Empty;
|
||||
//Bilder
|
||||
public List<string> Photos { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user