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
|
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 Mora = 202U; // 摩拉
|
||||||
public const uint WanderersAdvice = 104001U; // 流浪者的经验
|
public const uint WanderersAdvice = 104001U; // 流浪者的经验
|
||||||
public const uint AdventurersExperience = 104002U; // 冒险家的经验
|
public const uint AdventurersExperience = 104002U; // 冒险家的经验
|
||||||
|
|||||||
@@ -2435,6 +2435,15 @@ Space Available: {2}</value>
|
|||||||
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
|
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
|
||||||
<value>Material Statistics</value>
|
<value>Material Statistics</value>
|
||||||
</data>
|
</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">
|
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||||
<value>Go</value>
|
<value>Go</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -2486,6 +2486,15 @@
|
|||||||
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
|
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
|
||||||
<value>材料统计</value>
|
<value>材料统计</value>
|
||||||
</data>
|
</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">
|
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||||
<value>前往</value>
|
<value>前往</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Cultivation;
|
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));
|
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.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
|
using Snap.Hutao.Model;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Model.Entity.Primitive;
|
using Snap.Hutao.Model.Entity.Primitive;
|
||||||
using Snap.Hutao.Model.Intrinsic;
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
using Snap.Hutao.Model.Metadata;
|
using Snap.Hutao.Model.Metadata;
|
||||||
|
using Snap.Hutao.Model.Metadata.Item;
|
||||||
using Snap.Hutao.Model.Primitive;
|
using Snap.Hutao.Model.Primitive;
|
||||||
using Snap.Hutao.Service.Cultivation.Consumption;
|
using Snap.Hutao.Service.Cultivation.Consumption;
|
||||||
using Snap.Hutao.Service.Inventory;
|
using Snap.Hutao.Service.Inventory;
|
||||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||||
using Snap.Hutao.ViewModel.Cultivation;
|
using Snap.Hutao.ViewModel.Cultivation;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using ModelItem = Snap.Hutao.Model.Item;
|
using ModelItem = Snap.Hutao.Model.Item;
|
||||||
@@ -130,6 +135,7 @@ internal sealed partial class CultivationService : ICultivationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
CultivationStatisticsSurplusMerge.Apply(resultItems, context, mergeOptions);
|
CultivationStatisticsSurplusMerge.Apply(resultItems, context, mergeOptions);
|
||||||
|
ApplyStatisticsConsumerMenuText(resultItems, projectId, context, cultivationRepository);
|
||||||
|
|
||||||
return new(resultItems);
|
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);
|
ImmutableArray<CultivateEntry> GetCultivateEntryImmutableArrayByProjectIdAndItemId(Guid projectId, uint itemId);
|
||||||
|
|
||||||
Guid GetCultivateProjectIdByEntryId(Guid entryId);
|
Guid GetCultivateProjectIdByEntryId(Guid entryId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 联表查询某计划下所有养成物品及其所属条目(用于材料统计未勾选条目等)。
|
||||||
|
/// </summary>
|
||||||
|
ImmutableArray<(CultivateEntry Entry, CultivateItem Item)> GetCultivateEntryItemPairsByProjectId(Guid projectId);
|
||||||
}
|
}
|
||||||
@@ -227,7 +227,13 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="StatisticsItemTemplate" x:DataType="shvcu:StatisticsCultivateItem">
|
<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>
|
<shuxcc:HorizontalCard.Left>
|
||||||
<Grid Grid.Column="0">
|
<Grid Grid.Column="0">
|
||||||
<shuxc:ItemIcon
|
<shuxc:ItemIcon
|
||||||
@@ -293,6 +299,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</shuxcc:HorizontalCard.Right>
|
</shuxcc:HorizontalCard.Right>
|
||||||
</shuxcc:HorizontalCard>
|
</shuxcc:HorizontalCard>
|
||||||
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="InventoryItemTemplate" x:DataType="shvcu:InventoryItemView">
|
<DataTemplate x:Key="InventoryItemTemplate" x:DataType="shvcu:InventoryItemView">
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ internal sealed class StatisticsCultivateItem
|
|||||||
|
|
||||||
public bool IsToday { get => Inner.IsItemOfToday(offset, true); }
|
public bool IsToday { get => Inner.IsItemOfToday(offset, true); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 材料统计右键菜单中展示的「未完成」养成条目说明(含需求量)。
|
||||||
|
/// </summary>
|
||||||
|
public string StatisticsConsumerMenuText { get; set; } = string.Empty;
|
||||||
|
|
||||||
internal bool ExcludedFromPresentation { get; set; }
|
internal bool ExcludedFromPresentation { get; set; }
|
||||||
|
|
||||||
public static StatisticsCultivateItem Create(Material inner, TimeSpan offset)
|
public static StatisticsCultivateItem Create(Material inner, TimeSpan offset)
|
||||||
|
|||||||
Reference in New Issue
Block a user