mirror of
https://github.com/wangdage12/Snap.Hutao.git
synced 2026-07-31 04:10:25 +08:00
材料统计,右键 材料数字处,反推缺少人和数量
This commit is contained in:
@@ -10,6 +10,18 @@ namespace Snap.Hutao.Model.Metadata.Item;
|
||||
|
||||
internal static class MaterialIds
|
||||
{
|
||||
/// <summary>
|
||||
/// 材料统计右键「未完成条目」不追溯的材料(摩拉、经验书、精锻用魔矿)。
|
||||
/// </summary>
|
||||
public static bool IsExcludedFromStatisticsConsumerMenu(uint materialId)
|
||||
{
|
||||
return materialId is Mora
|
||||
or WanderersAdvice
|
||||
or AdventurersExperience
|
||||
or HeroesWit
|
||||
or MysticEnhancementOre;
|
||||
}
|
||||
|
||||
public const uint Mora = 202U; // 摩拉
|
||||
public const uint WanderersAdvice = 104001U; // 流浪者的经验
|
||||
public const uint AdventurersExperience = 104002U; // 冒险家的经验
|
||||
|
||||
@@ -2435,6 +2435,15 @@ Space Available: {2}</value>
|
||||
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
|
||||
<value>Material Statistics</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationStatisticsUnfinishedConsumers" xml:space="preserve">
|
||||
<value>Unfinished: {0}</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationStatisticsUnfinishedConsumersEmptyList" xml:space="preserve">
|
||||
<value>No unchecked entries</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationStatisticsConsumerMenuExcluded" xml:space="preserve">
|
||||
<value>Mora, EXP books, and Mystic Enhancement Ore have no per-entry breakdown.</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||
<value>Go</value>
|
||||
</data>
|
||||
|
||||
@@ -2486,6 +2486,15 @@
|
||||
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
|
||||
<value>材料统计</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationStatisticsUnfinishedConsumers" xml:space="preserve">
|
||||
<value>未完成:{0}</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationStatisticsUnfinishedConsumersEmptyList" xml:space="preserve">
|
||||
<value>无未勾选条目</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationStatisticsConsumerMenuExcluded" xml:space="preserve">
|
||||
<value>摩拉、经验书与魔矿不显示未完成条目。</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||
<value>前往</value>
|
||||
</data>
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
@@ -96,4 +100,23 @@ internal sealed partial class CultivationRepository : ICultivationRepository
|
||||
{
|
||||
return this.Single<CultivateEntry, Guid>(query => query.Where(entry => entry.InnerId == entryId).Select(entry => entry.InnerId));
|
||||
}
|
||||
|
||||
public ImmutableArray<(CultivateEntry Entry, CultivateItem Item)> GetCultivateEntryItemPairsByProjectId(Guid projectId)
|
||||
{
|
||||
using (IServiceScope scope = ServiceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext db = scope.GetAppDbContext();
|
||||
IQueryable<CultivateEntry> entries = db.Set<CultivateEntry>().AsNoTracking().Where(e => e.ProjectId == projectId);
|
||||
return [.. db.Set<CultivateItem>().AsNoTracking()
|
||||
.Join(
|
||||
entries,
|
||||
item => item.EntryId,
|
||||
entry => entry.InnerId,
|
||||
(item, entry) => new { Entry = entry, Item = item })
|
||||
.OrderBy(t => t.Entry.InnerId)
|
||||
.ThenBy(t => t.Item.ItemId)
|
||||
.ToList()
|
||||
.Select(t => (t.Entry, t.Item))];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,23 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Cultivation.Consumption;
|
||||
using Snap.Hutao.Service.Inventory;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.Cultivation;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using ModelItem = Snap.Hutao.Model.Item;
|
||||
@@ -130,6 +135,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
CultivationStatisticsSurplusMerge.Apply(resultItems, context, mergeOptions);
|
||||
ApplyStatisticsConsumerMenuText(resultItems, projectId, context, cultivationRepository);
|
||||
|
||||
return new(resultItems);
|
||||
}
|
||||
@@ -305,4 +311,63 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyStatisticsConsumerMenuText(
|
||||
Dictionary<uint, StatisticsCultivateItem> resultItems,
|
||||
Guid projectId,
|
||||
ICultivationMetadataContext context,
|
||||
ICultivationRepository cultivationRepository)
|
||||
{
|
||||
ImmutableArray<(CultivateEntry Entry, CultivateItem Item)> pairs = cultivationRepository.GetCultivateEntryItemPairsByProjectId(projectId);
|
||||
Dictionary<uint, List<string>> unfinishedSegmentsByMaterial = [];
|
||||
|
||||
foreach ((CultivateEntry entry, CultivateItem item) in pairs.AsSpan())
|
||||
{
|
||||
if (item.IsFinished)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string name = FormatStatisticsConsumerEntryName(entry, context);
|
||||
string segment = $"{name}×{item.Count}";
|
||||
ref List<string>? list = ref CollectionsMarshal.GetValueRefOrAddDefault(unfinishedSegmentsByMaterial, item.ItemId, out _);
|
||||
list ??= [];
|
||||
list.Add(segment);
|
||||
}
|
||||
|
||||
foreach ((uint materialId, StatisticsCultivateItem stat) in resultItems)
|
||||
{
|
||||
if (MaterialIds.IsExcludedFromStatisticsConsumerMenu(materialId))
|
||||
{
|
||||
stat.StatisticsConsumerMenuText = SH.ViewPageCultivationStatisticsConsumerMenuExcluded;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unfinishedSegmentsByMaterial.TryGetValue(materialId, out List<string>? segments))
|
||||
{
|
||||
segments.Sort(StringComparer.Ordinal);
|
||||
stat.StatisticsConsumerMenuText = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SH.ViewPageCultivationStatisticsUnfinishedConsumers,
|
||||
string.Join(',', segments));
|
||||
}
|
||||
else
|
||||
{
|
||||
stat.StatisticsConsumerMenuText = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SH.ViewPageCultivationStatisticsUnfinishedConsumers,
|
||||
SH.ViewPageCultivationStatisticsUnfinishedConsumersEmptyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatStatisticsConsumerEntryName(CultivateEntry entry, ICultivationMetadataContext context)
|
||||
{
|
||||
return entry.Type switch
|
||||
{
|
||||
CultivateType.AvatarAndSkill => context.GetAvatarItem(entry.Id).Name,
|
||||
CultivateType.Weapon => context.GetWeaponItem(entry.Id).Name,
|
||||
_ => Material.Default.Name,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -44,4 +44,9 @@ internal interface ICultivationRepository : IRepository<CultivateEntryLevelInfor
|
||||
ImmutableArray<CultivateEntry> GetCultivateEntryImmutableArrayByProjectIdAndItemId(Guid projectId, uint itemId);
|
||||
|
||||
Guid GetCultivateProjectIdByEntryId(Guid entryId);
|
||||
|
||||
/// <summary>
|
||||
/// 联表查询某计划下所有养成物品及其所属条目(用于材料统计未勾选条目等)。
|
||||
/// </summary>
|
||||
ImmutableArray<(CultivateEntry Entry, CultivateItem Item)> GetCultivateEntryItemPairsByProjectId(Guid projectId);
|
||||
}
|
||||
@@ -227,7 +227,13 @@
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="StatisticsItemTemplate" x:DataType="shvcu:StatisticsCultivateItem">
|
||||
<shuxcc:HorizontalCard Background="{Binding IsToday, Converter={ThemeResource BoolToStatisticsBrushSelector}}">
|
||||
<Border Background="Transparent" IsRightTapEnabled="True">
|
||||
<Border.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem IsEnabled="False" MaxWidth="360" Text="{Binding StatisticsConsumerMenuText, Mode=OneWay}"/>
|
||||
</MenuFlyout>
|
||||
</Border.ContextFlyout>
|
||||
<shuxcc:HorizontalCard Background="{Binding IsToday, Converter={ThemeResource BoolToStatisticsBrushSelector}}">
|
||||
<shuxcc:HorizontalCard.Left>
|
||||
<Grid Grid.Column="0">
|
||||
<shuxc:ItemIcon
|
||||
@@ -293,6 +299,7 @@
|
||||
</Grid>
|
||||
</shuxcc:HorizontalCard.Right>
|
||||
</shuxcc:HorizontalCard>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="InventoryItemTemplate" x:DataType="shvcu:InventoryItemView">
|
||||
|
||||
@@ -66,6 +66,11 @@ internal sealed class StatisticsCultivateItem
|
||||
|
||||
public bool IsToday { get => Inner.IsItemOfToday(offset, true); }
|
||||
|
||||
/// <summary>
|
||||
/// 材料统计右键菜单中展示的「未完成」养成条目说明(含需求量)。
|
||||
/// </summary>
|
||||
public string StatisticsConsumerMenuText { get; set; } = string.Empty;
|
||||
|
||||
internal bool ExcludedFromPresentation { get; set; }
|
||||
|
||||
public static StatisticsCultivateItem Create(Material inner, TimeSpan offset)
|
||||
|
||||
Reference in New Issue
Block a user