更新
This commit is contained in:
@@ -25,6 +25,18 @@ public partial class WashStep : ObservableObject
|
||||
private bool _isActive;
|
||||
}
|
||||
|
||||
public sealed record PackageTimingProfile(
|
||||
int FirstSprayWaterTime,
|
||||
int AfterShampoo1SprayTime,
|
||||
int AfterShampoo2SprayTime,
|
||||
int AfterShampoo3SprayTime,
|
||||
int SprayShampoo1Time,
|
||||
int SprayShampoo2Time,
|
||||
int SprayShampoo3Time,
|
||||
int HotAirTime,
|
||||
int ColdAirTime,
|
||||
int UvSterilizationTime);
|
||||
|
||||
public partial class MainViewModel : ObservableObject
|
||||
{
|
||||
private readonly ApiService _apiService;
|
||||
@@ -34,6 +46,7 @@ public partial class MainViewModel : ObservableObject
|
||||
private readonly LogService _logger;
|
||||
|
||||
public event Action<string>? ViewChanged;
|
||||
public event Action? OpenPackageManagementRequested;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<Package> _packages = new();
|
||||
@@ -121,6 +134,42 @@ public partial class MainViewModel : ObservableObject
|
||||
[ObservableProperty]
|
||||
private bool _isCustomerServiceDialogOpen;
|
||||
|
||||
[ObservableProperty]
|
||||
private Package? _editingPackage;
|
||||
|
||||
[ObservableProperty]
|
||||
private decimal _editingPackagePrice = 0.01m;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageFirstSprayWaterTime = 2;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageAfterShampoo1SprayTime = 2;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageAfterShampoo2SprayTime = 2;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageAfterShampoo3SprayTime = 2;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageSprayShampoo1Time = 1;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageSprayShampoo2Time = 1;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageSprayShampoo3Time = 1;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageColdAirTime = 2;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageHotAirTime = 5;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _editingPackageUvSterilizationTime = 2;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _firstSprayWaterTime = 2;
|
||||
|
||||
@@ -179,6 +228,18 @@ public partial class MainViewModel : ObservableObject
|
||||
private bool _isPaymentPolling;
|
||||
private bool _isCheckingPaymentStatus;
|
||||
|
||||
public int EditingPackageTotalDuration =>
|
||||
EditingPackageFirstSprayWaterTime +
|
||||
EditingPackageAfterShampoo1SprayTime +
|
||||
EditingPackageAfterShampoo2SprayTime +
|
||||
EditingPackageAfterShampoo3SprayTime +
|
||||
EditingPackageSprayShampoo1Time +
|
||||
EditingPackageSprayShampoo2Time +
|
||||
EditingPackageSprayShampoo3Time +
|
||||
EditingPackageHotAirTime +
|
||||
EditingPackageColdAirTime +
|
||||
EditingPackageUvSterilizationTime;
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
_config = new ConfigurationService();
|
||||
@@ -458,6 +519,11 @@ public partial class MainViewModel : ObservableObject
|
||||
_logger.LogInfo($"切换到设置界面,之前的视图: {_previousView}");
|
||||
}
|
||||
|
||||
public void ShowSettingsFromAdmin()
|
||||
{
|
||||
ShowSettings();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ShowInstruction()
|
||||
{
|
||||
@@ -504,6 +570,7 @@ public partial class MainViewModel : ObservableObject
|
||||
_config.ColdAirTime = ColdAirTime;
|
||||
_config.HotAirTime = HotAirTime;
|
||||
_config.UvSterilizationTime = UvSterilizationTime;
|
||||
_config.Save();
|
||||
|
||||
_logger.LogInfo($"本地参数已更新 - 首次喷水:{FirstSprayWaterTime}min, 沐浴1后:{AfterShampoo1SprayTime}min, " +
|
||||
$"沐浴2后:{AfterShampoo2SprayTime}min, 沐浴3后:{AfterShampoo3SprayTime}min, " +
|
||||
@@ -570,6 +637,75 @@ public partial class MainViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
private PackageTimingProfile GetPackageTimingProfile(Package package)
|
||||
{
|
||||
if (package.FirstSprayWaterTime > 0 &&
|
||||
package.AfterShampoo1SprayTime > 0 &&
|
||||
package.AfterShampoo2SprayTime > 0 &&
|
||||
package.AfterShampoo3SprayTime > 0 &&
|
||||
package.SprayShampoo1Time > 0 &&
|
||||
package.SprayShampoo2Time > 0 &&
|
||||
package.SprayShampoo3Time > 0 &&
|
||||
package.HotAirTime > 0 &&
|
||||
package.ColdAirTime > 0 &&
|
||||
package.UvSterilizationTime > 0)
|
||||
{
|
||||
return new PackageTimingProfile(
|
||||
package.FirstSprayWaterTime,
|
||||
package.AfterShampoo1SprayTime,
|
||||
package.AfterShampoo2SprayTime,
|
||||
package.AfterShampoo3SprayTime,
|
||||
package.SprayShampoo1Time,
|
||||
package.SprayShampoo2Time,
|
||||
package.SprayShampoo3Time,
|
||||
package.HotAirTime,
|
||||
package.ColdAirTime,
|
||||
package.UvSterilizationTime);
|
||||
}
|
||||
|
||||
return new PackageTimingProfile(
|
||||
_config.FirstSprayWaterTime,
|
||||
_config.AfterShampoo1SprayTime,
|
||||
_config.AfterShampoo2SprayTime,
|
||||
_config.AfterShampoo3SprayTime,
|
||||
_config.SprayShampoo1Time,
|
||||
_config.SprayShampoo2Time,
|
||||
_config.SprayShampoo3Time,
|
||||
_config.HotAirTime,
|
||||
_config.ColdAirTime,
|
||||
_config.UvSterilizationTime);
|
||||
}
|
||||
|
||||
private void ApplyPackageTimingProfile(PackageTimingProfile profile)
|
||||
{
|
||||
FirstSprayWaterTime = profile.FirstSprayWaterTime;
|
||||
AfterShampoo1SprayTime = profile.AfterShampoo1SprayTime;
|
||||
AfterShampoo2SprayTime = profile.AfterShampoo2SprayTime;
|
||||
AfterShampoo3SprayTime = profile.AfterShampoo3SprayTime;
|
||||
SprayShampoo1Time = profile.SprayShampoo1Time;
|
||||
SprayShampoo2Time = profile.SprayShampoo2Time;
|
||||
SprayShampoo3Time = profile.SprayShampoo3Time;
|
||||
HotAirTime = profile.HotAirTime;
|
||||
ColdAirTime = profile.ColdAirTime;
|
||||
UvSterilizationTime = profile.UvSterilizationTime;
|
||||
}
|
||||
|
||||
private async Task ApplyPackageTimingProfileAsync(Package package)
|
||||
{
|
||||
var profile = GetPackageTimingProfile(package);
|
||||
ApplyPackageTimingProfile(profile);
|
||||
|
||||
_logger.LogInfo(
|
||||
$"套餐参数已切换: PackageId={package.Id}, FirstSpray={FirstSprayWaterTime}, Shampoo1={SprayShampoo1Time}, " +
|
||||
$"Rinse1={AfterShampoo1SprayTime}, Shampoo2={SprayShampoo2Time}, Rinse2={AfterShampoo2SprayTime}, " +
|
||||
$"Conditioner={SprayShampoo3Time}, Rinse3={AfterShampoo3SprayTime}, HotAir={HotAirTime}, ColdAir={ColdAirTime}, UV={UvSterilizationTime}");
|
||||
|
||||
if (IsModbusConnected)
|
||||
{
|
||||
await SaveParametersToDeviceAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// 参数调整命令
|
||||
[RelayCommand]
|
||||
private void IncreaseFirstSprayWater() => FirstSprayWaterTime = Math.Min(FirstSprayWaterTime + 1, 60);
|
||||
@@ -695,30 +831,7 @@ public partial class MainViewModel : ObservableObject
|
||||
|
||||
try
|
||||
{
|
||||
// 创建订单
|
||||
var createOrderResponse = await _apiService.CreateOrderAsync(package.Id);
|
||||
CurrentOrder = createOrderResponse?.Order;
|
||||
PaymentCodeUrl = createOrderResponse?.CodeUrl ?? "";
|
||||
PaymentOutTradeNo = createOrderResponse?.OutTradeNo ?? "";
|
||||
PaymentExpiresAt = createOrderResponse?.ExpiresAt;
|
||||
IsPaymentExpired = false;
|
||||
PaymentStatusText = "等待扫码支付";
|
||||
RefreshPaymentCountdown();
|
||||
PaymentQrCodeImage = string.IsNullOrWhiteSpace(PaymentCodeUrl)
|
||||
? null
|
||||
: BuildPaymentQrCodeImage(PaymentCodeUrl);
|
||||
|
||||
if (CurrentOrder == null || PaymentQrCodeImage == null || string.IsNullOrWhiteSpace(PaymentOutTradeNo))
|
||||
{
|
||||
throw new InvalidOperationException("Payment QR code was not returned by the server.");
|
||||
}
|
||||
StatusMessage = $"订单创建成功,请扫码支付 ¥{package.Price}";
|
||||
_logger.LogInfo($"订单创建成功,订单ID: {CurrentOrder?.Id}");
|
||||
|
||||
// 切换到二维码支付界面
|
||||
CurrentView = "QRCode";
|
||||
ViewChanged?.Invoke("QRCode");
|
||||
StartPaymentStatusPolling();
|
||||
await CreatePaymentOrderAsync(package, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -748,28 +861,11 @@ public partial class MainViewModel : ObservableObject
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInfo($"创建订单,套餐ID: {SelectedPackage.Id}");
|
||||
var createOrderResponse = await _apiService.CreateOrderAsync(SelectedPackage.Id);
|
||||
CurrentOrder = createOrderResponse?.Order;
|
||||
PaymentCodeUrl = createOrderResponse?.CodeUrl ?? "";
|
||||
PaymentOutTradeNo = createOrderResponse?.OutTradeNo ?? "";
|
||||
PaymentExpiresAt = createOrderResponse?.ExpiresAt;
|
||||
IsPaymentExpired = false;
|
||||
PaymentStatusText = "等待扫码支付";
|
||||
RefreshPaymentCountdown();
|
||||
PaymentQrCodeImage = string.IsNullOrWhiteSpace(PaymentCodeUrl)
|
||||
? null
|
||||
: BuildPaymentQrCodeImage(PaymentCodeUrl);
|
||||
|
||||
if (CurrentOrder == null || PaymentQrCodeImage == null || string.IsNullOrWhiteSpace(PaymentOutTradeNo))
|
||||
{
|
||||
throw new InvalidOperationException("Payment QR code was not returned by the server.");
|
||||
}
|
||||
StatusMessage = $"订单创建成功,订单号: {CurrentOrder?.Id},请支付 ¥{SelectedPackage.Price}";
|
||||
_logger.LogInfo($"订单创建成功,订单ID: {CurrentOrder?.Id}");
|
||||
await CreatePaymentOrderAsync(SelectedPackage, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StopPaymentStatusPolling();
|
||||
_logger.LogError("创建订单失败", ex);
|
||||
StatusMessage = $"创建订单失败: {ex.Message}";
|
||||
MessageBox.Show($"创建订单失败\n\n{ex.Message}", "错误",
|
||||
@@ -808,6 +904,71 @@ public partial class MainViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RegeneratePaymentQrAsync()
|
||||
{
|
||||
if (SelectedPackage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
StopPaymentStatusPolling();
|
||||
PaymentStatusText = "正在刷新二维码...";
|
||||
StatusMessage = "正在刷新二维码...";
|
||||
|
||||
if (CurrentOrder != null && !CurrentOrder.IsPaid)
|
||||
{
|
||||
await _apiService.UpdateOrderStatusAsync(CurrentOrder.Id, OrderStatus.Cancelled);
|
||||
}
|
||||
|
||||
await CreatePaymentOrderAsync(SelectedPackage, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("刷新支付二维码失败", ex);
|
||||
PaymentStatusText = "二维码刷新失败";
|
||||
StatusMessage = $"二维码刷新失败: {ex.Message}";
|
||||
MessageBox.Show($"二维码刷新失败\n\n{ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreatePaymentOrderAsync(Package package, bool navigateToQrCode)
|
||||
{
|
||||
_logger.LogInfo($"创建订单,套餐ID: {package.Id}");
|
||||
await ApplyPackageTimingProfileAsync(package);
|
||||
|
||||
var createOrderResponse = await _apiService.CreateOrderAsync(package.Id);
|
||||
CurrentOrder = createOrderResponse?.Order;
|
||||
PaymentCodeUrl = createOrderResponse?.CodeUrl ?? "";
|
||||
PaymentOutTradeNo = createOrderResponse?.OutTradeNo ?? "";
|
||||
PaymentExpiresAt = createOrderResponse?.ExpiresAt;
|
||||
IsPaymentExpired = false;
|
||||
PaymentStatusText = "等待扫码支付";
|
||||
RefreshPaymentCountdown();
|
||||
PaymentQrCodeImage = string.IsNullOrWhiteSpace(PaymentCodeUrl)
|
||||
? null
|
||||
: BuildPaymentQrCodeImage(PaymentCodeUrl);
|
||||
|
||||
if (CurrentOrder == null || PaymentQrCodeImage == null || string.IsNullOrWhiteSpace(PaymentOutTradeNo))
|
||||
{
|
||||
throw new InvalidOperationException("Payment QR code was not returned by the server.");
|
||||
}
|
||||
|
||||
StatusMessage = $"订单创建成功,请扫码支付 ¥{package.DisplayPrice}";
|
||||
_logger.LogInfo($"订单创建成功,订单ID: {CurrentOrder?.Id}");
|
||||
|
||||
if (navigateToQrCode)
|
||||
{
|
||||
CurrentView = "QRCode";
|
||||
ViewChanged?.Invoke("QRCode");
|
||||
}
|
||||
|
||||
StartPaymentStatusPolling();
|
||||
}
|
||||
|
||||
private static BitmapImage BuildPaymentQrCodeImage(string codeUrl)
|
||||
{
|
||||
using var generator = new QRCodeGenerator();
|
||||
@@ -1379,6 +1540,154 @@ public partial class MainViewModel : ObservableObject
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenPackageManagement()
|
||||
{
|
||||
if (Packages.Count == 0)
|
||||
{
|
||||
StatusMessage = "套餐列表未加载";
|
||||
return;
|
||||
}
|
||||
|
||||
EditingPackage ??= Packages[0];
|
||||
OpenPackageManagementRequested?.Invoke();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SavePackageConfigAsync()
|
||||
{
|
||||
if (EditingPackage == null)
|
||||
{
|
||||
MessageBox.Show("请先选择要编辑的套餐。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurrentOrder != null && !CurrentOrder.IsPaid)
|
||||
{
|
||||
MessageBox.Show("当前有未完成支付订单,不能修改套餐配置。", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (EditingPackagePrice <= 0 ||
|
||||
EditingPackageFirstSprayWaterTime <= 0 ||
|
||||
EditingPackageAfterShampoo1SprayTime <= 0 ||
|
||||
EditingPackageAfterShampoo2SprayTime <= 0 ||
|
||||
EditingPackageAfterShampoo3SprayTime <= 0 ||
|
||||
EditingPackageSprayShampoo1Time <= 0 ||
|
||||
EditingPackageSprayShampoo2Time <= 0 ||
|
||||
EditingPackageSprayShampoo3Time <= 0 ||
|
||||
EditingPackageHotAirTime <= 0 ||
|
||||
EditingPackageColdAirTime <= 0 ||
|
||||
EditingPackageUvSterilizationTime <= 0)
|
||||
{
|
||||
MessageBox.Show("套餐金额和每个流程时间都必须大于 0。", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
StatusMessage = $"正在保存 {EditingPackage.Name} 配置...";
|
||||
|
||||
var packageToSave = new Package
|
||||
{
|
||||
Id = EditingPackage.Id,
|
||||
Name = EditingPackage.Name,
|
||||
Description = EditingPackage.Description,
|
||||
Price = EditingPackagePrice,
|
||||
DurationMinutes = EditingPackageTotalDuration,
|
||||
FirstSprayWaterTime = EditingPackageFirstSprayWaterTime,
|
||||
AfterShampoo1SprayTime = EditingPackageAfterShampoo1SprayTime,
|
||||
AfterShampoo2SprayTime = EditingPackageAfterShampoo2SprayTime,
|
||||
AfterShampoo3SprayTime = EditingPackageAfterShampoo3SprayTime,
|
||||
SprayShampoo1Time = EditingPackageSprayShampoo1Time,
|
||||
SprayShampoo2Time = EditingPackageSprayShampoo2Time,
|
||||
SprayShampoo3Time = EditingPackageSprayShampoo3Time,
|
||||
HotAirTime = EditingPackageHotAirTime,
|
||||
ColdAirTime = EditingPackageColdAirTime,
|
||||
UvSterilizationTime = EditingPackageUvSterilizationTime
|
||||
};
|
||||
|
||||
var savedPackage = await _apiService.UpdatePackageAsync(packageToSave)
|
||||
?? throw new InvalidOperationException("Server did not return the updated package.");
|
||||
|
||||
ReplacePackageInCollection(savedPackage);
|
||||
EditingPackage = savedPackage;
|
||||
|
||||
if (SelectedPackage?.Id == savedPackage.Id)
|
||||
{
|
||||
SelectedPackage = savedPackage;
|
||||
await ApplyPackageTimingProfileAsync(savedPackage);
|
||||
}
|
||||
|
||||
if (CurrentOrder?.PackageId == savedPackage.Id && CurrentOrder.IsPaid)
|
||||
{
|
||||
CurrentOrder.Package = savedPackage;
|
||||
}
|
||||
|
||||
StatusMessage = $"{savedPackage.Name} 配置已保存";
|
||||
MessageBox.Show(
|
||||
$"{savedPackage.Name} 已更新。\n\n金额: {savedPackage.DisplayPrice} 元\n总时长: {savedPackage.DurationMinutes} 分钟",
|
||||
"保存成功",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("保存套餐配置失败", ex);
|
||||
StatusMessage = $"保存套餐配置失败: {ex.Message}";
|
||||
MessageBox.Show($"保存套餐配置失败\n\n{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnEditingPackageChanged(Package? value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
LoadEditingPackage(value);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnEditingPackageFirstSprayWaterTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageAfterShampoo1SprayTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageAfterShampoo2SprayTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageAfterShampoo3SprayTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageSprayShampoo1TimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageSprayShampoo2TimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageSprayShampoo3TimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageHotAirTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageColdAirTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
partial void OnEditingPackageUvSterilizationTimeChanged(int value) => OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
|
||||
private void ReplacePackageInCollection(Package updatedPackage)
|
||||
{
|
||||
for (var i = 0; i < Packages.Count; i++)
|
||||
{
|
||||
if (Packages[i].Id == updatedPackage.Id)
|
||||
{
|
||||
Packages[i] = updatedPackage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Packages.Add(updatedPackage);
|
||||
}
|
||||
|
||||
private void LoadEditingPackage(Package package)
|
||||
{
|
||||
EditingPackagePrice = package.Price;
|
||||
EditingPackageFirstSprayWaterTime = package.FirstSprayWaterTime;
|
||||
EditingPackageAfterShampoo1SprayTime = package.AfterShampoo1SprayTime;
|
||||
EditingPackageAfterShampoo2SprayTime = package.AfterShampoo2SprayTime;
|
||||
EditingPackageAfterShampoo3SprayTime = package.AfterShampoo3SprayTime;
|
||||
EditingPackageSprayShampoo1Time = package.SprayShampoo1Time;
|
||||
EditingPackageSprayShampoo2Time = package.SprayShampoo2Time;
|
||||
EditingPackageSprayShampoo3Time = package.SprayShampoo3Time;
|
||||
EditingPackageHotAirTime = package.HotAirTime;
|
||||
EditingPackageColdAirTime = package.ColdAirTime;
|
||||
EditingPackageUvSterilizationTime = package.UvSterilizationTime;
|
||||
OnPropertyChanged(nameof(EditingPackageTotalDuration));
|
||||
}
|
||||
|
||||
private void UpdateTemperatures(string stepName, int currentTime, int totalTime)
|
||||
{
|
||||
// 根据不同步骤模拟温度变化
|
||||
|
||||
Reference in New Issue
Block a user