1
0
mirror of https://github.com/wangdage12/Snap.Hutao.git synced 2026-07-31 04:10:25 +08:00

在 我的角色 同步角色 批量添加或更新到当前养成计划时,增加 清空已经角色和武器信息 勾选框。如勾选该项目,在更新计划前会清空 培养计划中,角色和武器的信息。另外,当点 确认,更新完数据后,刷新现 养成计划。

This commit is contained in:
mfkvfhpdx
2026-05-04 11:16:01 +08:00
parent c6a2212caa
commit ed4626a74c
10 changed files with 105 additions and 4 deletions
@@ -1349,6 +1349,9 @@ Space Available: {2}</value>
<data name="ViewDialogCultivateBatchWeaponLevelTarget" xml:space="preserve"> <data name="ViewDialogCultivateBatchWeaponLevelTarget" xml:space="preserve">
<value>Weapon Target Level</value> <value>Weapon Target Level</value>
</data> </data>
<data name="ViewDialogCultivateBatchClearAvatarAndWeaponEntries" xml:space="preserve">
<value>Clear existing character and weapon entries before updating</value>
</data>
<data name="ViewDialogCultivateProjectInputPlaceholder" xml:space="preserve"> <data name="ViewDialogCultivateProjectInputPlaceholder" xml:space="preserve">
<value>Enter the plan name here</value> <value>Enter the plan name here</value>
</data> </data>
@@ -1391,6 +1391,9 @@
<data name="ViewDialogCultivateBatchWeaponLevelTarget" xml:space="preserve"> <data name="ViewDialogCultivateBatchWeaponLevelTarget" xml:space="preserve">
<value>武器目标等级</value> <value>武器目标等级</value>
</data> </data>
<data name="ViewDialogCultivateBatchClearAvatarAndWeaponEntries" xml:space="preserve">
<value>更新计划前先清空已有角色与武器条目</value>
</data>
<data name="ViewDialogCultivateProjectInputPlaceholder" xml:space="preserve"> <data name="ViewDialogCultivateProjectInputPlaceholder" xml:space="preserve">
<value>在此处输入计划名称</value> <value>在此处输入计划名称</value>
</data> </data>
@@ -178,6 +178,48 @@ internal sealed partial class CultivationService : ICultivationService
cultivationRepository.RemoveCultivateEntryById(entryId); cultivationRepository.RemoveCultivateEntryById(entryId);
} }
public async ValueTask RemoveAvatarAndWeaponEntriesForCurrentProjectAsync()
{
IAdvancedDbCollectionView<CultivateProject> projects = await GetProjectCollectionAsync().ConfigureAwait(false);
if (!await EnsureCurrentProjectAsync(projects).ConfigureAwait(false))
{
return;
}
ArgumentNullException.ThrowIfNull(projects.CurrentItem);
Guid projectId = projects.CurrentItem.InnerId;
await taskContext.SwitchToBackgroundAsync();
ImmutableArray<CultivateEntry> entries = cultivationRepository.GetCultivateEntryImmutableArrayByProjectId(projectId);
List<Guid> weaponEntryIds = new(entries.Length);
List<Guid> avatarEntryIds = new(entries.Length);
foreach (ref readonly CultivateEntry entry in entries.AsSpan())
{
switch (entry.Type)
{
case CultivateType.Weapon:
weaponEntryIds.Add(entry.InnerId);
break;
case CultivateType.AvatarAndSkill:
avatarEntryIds.Add(entry.InnerId);
break;
}
}
foreach (Guid id in weaponEntryIds)
{
cultivationRepository.RemoveCultivateEntryById(id);
}
foreach (Guid id in avatarEntryIds)
{
cultivationRepository.RemoveCultivateEntryById(id);
}
entryCollectionCache.TryRemove(projectId, out _);
}
public void SaveCultivateItem(CultivateItemView item) public void SaveCultivateItem(CultivateItemView item)
{ {
cultivationRepository.UpdateCultivateItem(item.Entity); cultivationRepository.UpdateCultivateItem(item.Entity);
@@ -23,6 +23,11 @@ internal interface ICultivationService
ValueTask RemoveCultivateEntryAsync(Guid entryId); ValueTask RemoveCultivateEntryAsync(Guid entryId);
/// <summary>
/// 移除当前选中养成计划中角色与武器类型的全部养成条目。
/// </summary>
ValueTask RemoveAvatarAndWeaponEntriesForCurrentProjectAsync();
ValueTask RemoveProjectAsync(CultivateProject project); ValueTask RemoveProjectAsync(CultivateProject project);
ValueTask<ConsumptionSaveResult> SaveConsumptionAsync(InputConsumption inputConsumption); ValueTask<ConsumptionSaveResult> SaveConsumptionAsync(InputConsumption inputConsumption);
@@ -77,5 +77,10 @@
<RadioButton Content="{shuxm:ResourceString Name=ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting}"/> <RadioButton Content="{shuxm:ResourceString Name=ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting}"/>
<RadioButton Content="{shuxm:ResourceString Name=ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry}"/> <RadioButton Content="{shuxm:ResourceString Name=ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry}"/>
</RadioButtons> </RadioButtons>
<CheckBox
Margin="0,13,0,0"
Content="{shuxm:ResourceString Name=ViewDialogCultivateBatchClearAvatarAndWeaponEntries}"
IsChecked="{x:Bind ClearAvatarAndWeaponEntriesBeforeSync, Mode=TwoWay}"/>
</StackPanel> </StackPanel>
</ContentDialog> </ContentDialog>
@@ -10,6 +10,7 @@ using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
namespace Snap.Hutao.UI.Xaml.View.Dialog; namespace Snap.Hutao.UI.Xaml.View.Dialog;
[DependencyProperty<AvatarPromotionDelta>("PromotionDelta", NotNull = true, CreateDefaultValueCallbackName = nameof(CreatePromotionDeltaDefaultValue))] [DependencyProperty<AvatarPromotionDelta>("PromotionDelta", NotNull = true, CreateDefaultValueCallbackName = nameof(CreatePromotionDeltaDefaultValue))]
[DependencyProperty<bool>("ClearAvatarAndWeaponEntriesBeforeSync", DefaultValue = false, NotNull = true)]
internal sealed partial class CultivatePromotionDeltaBatchDialog : ContentDialog internal sealed partial class CultivatePromotionDeltaBatchDialog : ContentDialog
{ {
private readonly IContentDialogFactory contentDialogFactory; private readonly IContentDialogFactory contentDialogFactory;
@@ -45,7 +46,7 @@ internal sealed partial class CultivatePromotionDeltaBatchDialog : ContentDialog
LocalSetting.Set(SettingKeys.CultivationWeapon90LevelTarget, weapon.LevelTarget); LocalSetting.Set(SettingKeys.CultivationWeapon90LevelTarget, weapon.LevelTarget);
} }
return new(true, new(PromotionDelta, (ConsumptionSaveStrategyKind)SaveModeSelector.SelectedIndex)); return new(true, new CultivatePromotionDeltaOptions(PromotionDelta, (ConsumptionSaveStrategyKind)SaveModeSelector.SelectedIndex, ClearAvatarAndWeaponEntriesBeforeSync));
} }
private static object CreatePromotionDeltaDefaultValue() private static object CreatePromotionDeltaDefaultValue()
@@ -8,7 +8,7 @@ namespace Snap.Hutao.UI.Xaml.View.Dialog;
internal sealed class CultivatePromotionDeltaOptions internal sealed class CultivatePromotionDeltaOptions
{ {
public CultivatePromotionDeltaOptions(AvatarPromotionDelta delta, ConsumptionSaveStrategyKind strategy) public CultivatePromotionDeltaOptions(AvatarPromotionDelta delta, ConsumptionSaveStrategyKind strategy, bool clearAvatarAndWeaponEntriesBeforeSync = false)
{ {
delta.AvatarLevelTarget = delta.AvatarLevelTarget switch delta.AvatarLevelTarget = delta.AvatarLevelTarget switch
{ {
@@ -19,9 +19,15 @@ internal sealed class CultivatePromotionDeltaOptions
Delta = delta; Delta = delta;
Strategy = strategy; Strategy = strategy;
ClearAvatarAndWeaponEntriesBeforeSync = clearAvatarAndWeaponEntriesBeforeSync;
} }
public AvatarPromotionDelta Delta { get; } public AvatarPromotionDelta Delta { get; }
public ConsumptionSaveStrategyKind Strategy { get; } public ConsumptionSaveStrategyKind Strategy { get; }
/// <summary>
/// 批量同步前是否清空当前计划中已有的角色与武器养成条目(不含家具等其它类型)。
/// </summary>
public bool ClearAvatarAndWeaponEntriesBeforeSync { get; }
} }
@@ -21,6 +21,7 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User; using Snap.Hutao.Service.User;
using Snap.Hutao.UI.Xaml.Control.AutoSuggestBox; using Snap.Hutao.UI.Xaml.Control.AutoSuggestBox;
using Snap.Hutao.UI.Xaml.View.Dialog; using Snap.Hutao.UI.Xaml.View.Dialog;
using Snap.Hutao.ViewModel.Cultivation;
using Snap.Hutao.ViewModel.User; using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -286,6 +287,11 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
BatchCultivateResult result = default; BatchCultivateResult result = default;
using (await scopeContext.ContentDialogFactory.BlockAsync(progressDialog).ConfigureAwait(false)) using (await scopeContext.ContentDialogFactory.BlockAsync(progressDialog).ConfigureAwait(false))
{ {
if (baseline.ClearAvatarAndWeaponEntriesBeforeSync)
{
await scopeContext.CultivationService.RemoveAvatarAndWeaponEntriesForCurrentProjectAsync().ConfigureAwait(false);
}
ImmutableArray<CalculatorAvatarPromotionDelta>.Builder deltasBuilder = ImmutableArray.CreateBuilder<CalculatorAvatarPromotionDelta>(); ImmutableArray<CalculatorAvatarPromotionDelta>.Builder deltasBuilder = ImmutableArray.CreateBuilder<CalculatorAvatarPromotionDelta>();
foreach (AvatarView avatar in targetAvatars) foreach (AvatarView avatar in targetAvatars)
{ {
@@ -305,7 +311,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in batchConsumption.Items.Zip(deltas)) foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in batchConsumption.Items.Zip(deltas))
{ {
if (!await SaveCultivationAsync(consumption, new(delta, baseline.Strategy), true).ConfigureAwait(false)) if (!await SaveCultivationAsync(consumption, new CultivatePromotionDeltaOptions(delta, baseline.Strategy), true).ConfigureAwait(false))
{ {
break; break;
} }
@@ -314,6 +320,8 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
} }
} }
scopeContext.Messenger.Send(CultivationProjectEntriesChangedMessage.Empty);
InfoBarMessage message = result.SkippedCount > 0 InfoBarMessage message = result.SkippedCount > 0
? InfoBarMessage.Warning(SH.FormatViewModelCultivationBatchAddIncompleted(result.SucceedCount, result.SkippedCount)) ? InfoBarMessage.Warning(SH.FormatViewModelCultivationBatchAddIncompleted(result.SucceedCount, result.SkippedCount))
: InfoBarMessage.Success(SH.FormatViewModelCultivationBatchAddCompleted(result.SucceedCount, result.SkippedCount)); : InfoBarMessage.Success(SH.FormatViewModelCultivationBatchAddCompleted(result.SucceedCount, result.SkippedCount));
@@ -0,0 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.ViewModel.Cultivation;
/// <summary>
/// 当前养成计划的条目在 UI 外被批量变更(例如从「我的角色」同步)后,通知养成页刷新列表与统计。
/// </summary>
internal sealed class CultivationProjectEntriesChangedMessage
{
public static readonly CultivationProjectEntriesChangedMessage Empty = new();
private CultivationProjectEntriesChangedMessage()
{
}
}
@@ -2,6 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core; using Snap.Hutao.Core;
using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Database;
@@ -29,7 +30,7 @@ namespace Snap.Hutao.ViewModel.Cultivation;
[SuppressMessage("", "CA1001")] [SuppressMessage("", "CA1001")]
[BindableCustomPropertyProvider] [BindableCustomPropertyProvider]
[Service(ServiceLifetime.Scoped)] [Service(ServiceLifetime.Scoped)]
internal sealed partial class CultivationViewModel : Abstraction.ViewModel internal sealed partial class CultivationViewModel : Abstraction.ViewModel, IRecipient<CultivationProjectEntriesChangedMessage>
{ {
private readonly ExclusiveTokenProvider exclusiveTokenProvider = new(); private readonly ExclusiveTokenProvider exclusiveTokenProvider = new();
@@ -132,6 +133,17 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
UpdateEntryCollectionAsync(Projects?.CurrentItem).SafeForget(); UpdateEntryCollectionAsync(Projects?.CurrentItem).SafeForget();
} }
public async void Receive(CultivationProjectEntriesChangedMessage message)
{
await taskContext.SwitchToMainThreadAsync();
if (Projects?.CurrentItem is null)
{
return;
}
await UpdateEntryCollectionAsync(Projects.CurrentItem).ConfigureAwait(false);
}
[Command("AddProjectCommand")] [Command("AddProjectCommand")]
private async Task AddProjectAsync() private async Task AddProjectAsync()
{ {