Обновления веб-отображения
This commit is contained in:
@@ -134,7 +134,7 @@ public class FullEntity
|
|||||||
.Include(d => d.DeitySpells)
|
.Include(d => d.DeitySpells)
|
||||||
.ThenInclude(ds => ds.Spell)
|
.ThenInclude(ds => ds.Spell)
|
||||||
.Include(d => d.DeityWeapons)
|
.Include(d => d.DeityWeapons)
|
||||||
.ThenInclude(dw => dw.Weapon)
|
//.ThenInclude(dw => dw.Weapon)
|
||||||
.Include(d => d.CategoryEntity)
|
.Include(d => d.CategoryEntity)
|
||||||
.Include(d => d.Skills)
|
.Include(d => d.Skills)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Pf2e.Database.Model;
|
||||||
|
using Pf2e.Database.Model.Base.Keywords;
|
||||||
|
using Pf2e.Database.Model.Base.Source;
|
||||||
|
using Pf2e.Database.Model.Base.Targets;
|
||||||
|
using Pf2e.Database.Model.Common.I18n;
|
||||||
|
using Pf2e.Database.Model.Enum;
|
||||||
|
|
||||||
|
namespace Pf2e.Web.Helpers;
|
||||||
|
|
||||||
|
public class EntityFieldFormatter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Форматировать значение для отображения.
|
||||||
|
/// </summary>
|
||||||
|
public string? FormatValue(object? value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Type type = value.GetType();
|
||||||
|
|
||||||
|
foreach (ValueFormatter formatter in _formatters)
|
||||||
|
{
|
||||||
|
if (formatter.CanFormat(type))
|
||||||
|
{
|
||||||
|
return formatter.Format(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Абстрактный класс форматтера значений.
|
||||||
|
/// </summary>
|
||||||
|
private abstract class ValueFormatter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Проверяет, может ли форматтер обработать указанный тип.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Тип значения.</param>
|
||||||
|
/// <returns><c>true</c>, если форматтер может обработать тип, иначе <c>false</c>.</returns>
|
||||||
|
public abstract bool CanFormat(Type type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Форматирует значение для отображения.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Значение для форматирования.</param>
|
||||||
|
/// <returns>Строковое представление отформатированного значения.</returns>
|
||||||
|
public abstract string Format(object value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Форматтер для конкретного типа.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Тип значения.</typeparam>
|
||||||
|
/// <param name="formatter">Функция форматирования значения.</param>
|
||||||
|
private class TypeValueFormatter<T>(Func<object, string> formatter) : ValueFormatter
|
||||||
|
{
|
||||||
|
public override bool CanFormat(Type type) => type == typeof(T);
|
||||||
|
|
||||||
|
public override string Format(object value) => formatter(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Форматтер с пользовательскими функциями проверки и форматирования.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="checker">Функция проверки типа.</param>
|
||||||
|
/// <param name="formatter">Функция форматирования значения.</param>
|
||||||
|
private class CustomValueFormatter(Func<Type, bool> checker, Func<object, string> formatter)
|
||||||
|
: ValueFormatter
|
||||||
|
{
|
||||||
|
public override bool CanFormat(Type type) => checker(type);
|
||||||
|
|
||||||
|
public override string Format(object value) => formatter(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Форматтер для коллекций.
|
||||||
|
/// </summary>
|
||||||
|
private class CollectionFormatter(EntityFieldFormatter fieldFormatter) : ValueFormatter
|
||||||
|
{
|
||||||
|
public override bool CanFormat(Type type) =>
|
||||||
|
typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
|
||||||
|
|
||||||
|
public override string Format(object value)
|
||||||
|
{
|
||||||
|
IEnumerable collection = (IEnumerable)value;
|
||||||
|
List<string> items = [];
|
||||||
|
|
||||||
|
foreach (object? item in collection)
|
||||||
|
{
|
||||||
|
string? itemDisplay = fieldFormatter.FormatValue(item);
|
||||||
|
items.Add(itemDisplay ?? "null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.Count == 0 ? "(empty)" : string.Join("<br/>", items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Коллекция форматтеров.
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<ValueFormatter> _formatters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить описание элемента перечисления из DescriptionAttribute.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Элемент перечисления.</param>
|
||||||
|
/// <returns>Описание или имя элемента, если атрибут не найден.</returns>
|
||||||
|
private static string GetEnumDescription(Enum value)
|
||||||
|
{
|
||||||
|
Type type = value.GetType();
|
||||||
|
string name = Enum.GetName(type, value) ?? value.ToString();
|
||||||
|
|
||||||
|
System.Reflection.FieldInfo? field = type.GetField(name);
|
||||||
|
if (field == null)
|
||||||
|
return value.ToString();
|
||||||
|
|
||||||
|
if (
|
||||||
|
Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
|
||||||
|
is DescriptionAttribute attr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return attr.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Конструктор EntitySerializer.
|
||||||
|
/// </summary>
|
||||||
|
public EntityFieldFormatter()
|
||||||
|
{
|
||||||
|
_formatters =
|
||||||
|
[
|
||||||
|
// Форматтеры для перечислений
|
||||||
|
new CustomValueFormatter(
|
||||||
|
(type) => type.IsEnum,
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
Enum enumValue = (Enum)value;
|
||||||
|
string description = GetEnumDescription(enumValue);
|
||||||
|
return $"{description} ({enumValue})";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// Примитивные типы
|
||||||
|
new CustomValueFormatter(
|
||||||
|
(type) => type.IsPrimitive || type == typeof(string) || type == typeof(decimal),
|
||||||
|
(value) => value.ToString() ?? "null"
|
||||||
|
),
|
||||||
|
// LocaleString
|
||||||
|
new TypeValueFormatter<LocaleString>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
LocaleString localeString = (LocaleString)value;
|
||||||
|
List<string> items = [];
|
||||||
|
foreach (LocaleItem item in localeString)
|
||||||
|
{
|
||||||
|
items.Add($"{item.Locale}: {item.Value}");
|
||||||
|
}
|
||||||
|
return string.Join("<br/>", items);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// IEntity
|
||||||
|
new CustomValueFormatter(
|
||||||
|
(type) => typeof(IEntity).IsAssignableFrom(type),
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
IEntity entity = (IEntity)value;
|
||||||
|
EntityType entityType = entity.Entity.Type;
|
||||||
|
string typeName = PgNameHelper.GetPgName(entityType);
|
||||||
|
string slug = entity.Entity.Slug;
|
||||||
|
string name = entity.Entity.Name[LocaleType.English];
|
||||||
|
return $"<a href=\"/{typeName}/{slug}\">{name}</a>";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// EntitySource
|
||||||
|
new TypeValueFormatter<EntitySource>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
EntitySource entitySource = (EntitySource)value;
|
||||||
|
string sourceName = entitySource.Source.Name;
|
||||||
|
string suffix = !string.IsNullOrEmpty(entitySource.Suffix)
|
||||||
|
? $" ({entitySource.Suffix})"
|
||||||
|
: "";
|
||||||
|
string isRemaster = entitySource.IsRemaster ? " [Remaster]" : "";
|
||||||
|
return $"{sourceName}{suffix}{isRemaster}";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// Keyword
|
||||||
|
new TypeValueFormatter<Keyword>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
Keyword keyword = (Keyword)value;
|
||||||
|
string name = keyword.Name[LocaleType.English];
|
||||||
|
return $"{name}";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// KeywordAlias
|
||||||
|
new TypeValueFormatter<KeywordAlias>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
KeywordAlias alias = (KeywordAlias)value;
|
||||||
|
return $"{alias.Value}";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// ExternalTarget
|
||||||
|
new TypeValueFormatter<ExternalTarget>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
ExternalTarget target = (ExternalTarget)value;
|
||||||
|
return $"{target.Name} (Icon: {target.Icon}, Prefix: {target.Prefix})";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// EntityExternal
|
||||||
|
new TypeValueFormatter<EntityExternal>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
EntityExternal entityExternal = (EntityExternal)value;
|
||||||
|
string targetName = entityExternal.Target?.Name ?? "Unknown";
|
||||||
|
string externalId = !string.IsNullOrEmpty(entityExternal.ExternalId)
|
||||||
|
? $" ID: {entityExternal.ExternalId}"
|
||||||
|
: "";
|
||||||
|
string suffix = !string.IsNullOrEmpty(entityExternal.Suffix)
|
||||||
|
? $" Suffix: {entityExternal.Suffix}"
|
||||||
|
: "";
|
||||||
|
return $"{targetName}{externalId}{suffix}";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// Source
|
||||||
|
new TypeValueFormatter<Source>(
|
||||||
|
(value) =>
|
||||||
|
{
|
||||||
|
Source source = (Source)value;
|
||||||
|
string releaseDate = source.ReleaseDate?.ToString("yyyy-MM-dd") ?? "Unknown";
|
||||||
|
return $"{source.Name} (Category: {source.Category}, Release: {releaseDate})";
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// Коллекции
|
||||||
|
new CollectionFormatter(this),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Pf2e.Common;
|
using Pf2e.Common;
|
||||||
using Pf2e.Database.Model;
|
|
||||||
using Pf2e.Database.Model.Base;
|
|
||||||
using Pf2e.Database.Model.Common.I18n;
|
|
||||||
using Pf2e.Database.Model.Enum;
|
|
||||||
|
|
||||||
namespace Pf2e.Web.Helpers;
|
namespace Pf2e.Web.Helpers;
|
||||||
|
|
||||||
@@ -42,6 +37,11 @@ public class EntitySerializer
|
|||||||
public bool IsImplemented { get; set; } = true;
|
public bool IsImplemented { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Форматтер полей сущности.
|
||||||
|
/// </summary>
|
||||||
|
public EntityFieldFormatter FieldFormatter { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Сериализовать FullEntity.
|
/// Сериализовать FullEntity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -65,11 +65,6 @@ public class EntitySerializer
|
|||||||
result.Groups.Add(subEntityGroup);
|
result.Groups.Add(subEntityGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Группа для ссылок
|
|
||||||
FieldGroup referencesGroup = new() { Name = "Ссылки" };
|
|
||||||
AddReferencesToGroup(referencesGroup, fullEntity);
|
|
||||||
result.Groups.Add(referencesGroup);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,162 +82,29 @@ public class EntitySerializer
|
|||||||
if (property.Name == "Entity")
|
if (property.Name == "Entity")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FieldInfo fieldInfo = new()
|
object? value = property.GetValue(obj);
|
||||||
{
|
FieldInfo fieldInfo = CreateFieldInfo(property, value);
|
||||||
Name = property.Name,
|
|
||||||
Type = GetTypeDisplayName(property.PropertyType),
|
|
||||||
Value = property.GetValue(obj),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Обработка значения для отображения
|
|
||||||
var (displayValue, isImplemented) = FormatValue(fieldInfo.Value);
|
|
||||||
fieldInfo.DisplayValue = displayValue;
|
|
||||||
fieldInfo.IsImplemented = isImplemented;
|
|
||||||
|
|
||||||
group.Fields.Add(fieldInfo);
|
group.Fields.Add(fieldInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить ссылки в группу.
|
/// Создать FieldInfo для свойства.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AddReferencesToGroup(FieldGroup group, FullEntity fullEntity)
|
/// <param name="property">Поле класса.</param>
|
||||||
|
/// <param name="value">Значение поля.</param>
|
||||||
|
/// <returns>Информация о поле.</returns>
|
||||||
|
private FieldInfo CreateFieldInfo(PropertyInfo property, object? value)
|
||||||
{
|
{
|
||||||
// Добавляем информацию о связанных сущностях
|
string? displayValue = FieldFormatter.FormatValue(value);
|
||||||
Entity entity = fullEntity.Entity;
|
|
||||||
|
|
||||||
// Traits
|
return new()
|
||||||
if (entity.Traits?.Any() == true)
|
|
||||||
{
|
{
|
||||||
var (displayValue, isImplemented) = FormatValue(entity.Traits);
|
Name = property.Name,
|
||||||
group.Fields.Add(
|
Type = property.PropertyType.Name,
|
||||||
new FieldInfo
|
Value = value,
|
||||||
{
|
DisplayValue = displayValue ?? "null",
|
||||||
Name = "Traits",
|
IsImplemented = displayValue is not null,
|
||||||
Type = "ICollection<Trait>",
|
};
|
||||||
Value = entity.Traits,
|
|
||||||
DisplayValue = displayValue,
|
|
||||||
IsImplemented = isImplemented,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keywords
|
|
||||||
if (entity.Keywords?.Any() == true)
|
|
||||||
{
|
|
||||||
var (displayValue, isImplemented) = FormatValue(entity.Keywords);
|
|
||||||
group.Fields.Add(
|
|
||||||
new FieldInfo
|
|
||||||
{
|
|
||||||
Name = "Keywords",
|
|
||||||
Type = "ICollection<Keyword>",
|
|
||||||
Value = entity.Keywords,
|
|
||||||
DisplayValue = displayValue,
|
|
||||||
IsImplemented = isImplemented,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sources
|
|
||||||
if (entity.Sources?.Any() == true)
|
|
||||||
{
|
|
||||||
var (displayValue, isImplemented) = FormatValue(entity.Sources);
|
|
||||||
group.Fields.Add(
|
|
||||||
new FieldInfo
|
|
||||||
{
|
|
||||||
Name = "Sources",
|
|
||||||
Type = "ICollection<EntitySource>",
|
|
||||||
Value = entity.Sources,
|
|
||||||
DisplayValue = displayValue,
|
|
||||||
IsImplemented = isImplemented,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Externals
|
|
||||||
if (entity.Externals?.Any() == true)
|
|
||||||
{
|
|
||||||
var (displayValue, isImplemented) = FormatValue(entity.Externals);
|
|
||||||
group.Fields.Add(
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Name = "Externals",
|
|
||||||
Type = "ICollection<EntityExternal>",
|
|
||||||
Value = entity.Externals,
|
|
||||||
DisplayValue = displayValue,
|
|
||||||
IsImplemented = isImplemented,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить отображаемое имя типа.
|
|
||||||
/// </summary>
|
|
||||||
private string GetTypeDisplayName(Type type)
|
|
||||||
{
|
|
||||||
if (type.IsEnum)
|
|
||||||
return "enum";
|
|
||||||
|
|
||||||
return type.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Форматировать значение для отображения.
|
|
||||||
/// </summary>
|
|
||||||
private (string displayValue, bool isImplemented) FormatValue(object? value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
return ("null", true);
|
|
||||||
|
|
||||||
Type type = value.GetType();
|
|
||||||
|
|
||||||
// Обработка перечислений
|
|
||||||
if (type.IsEnum)
|
|
||||||
{
|
|
||||||
Enum enumValue = (Enum)value;
|
|
||||||
return ($"{enumValue} ({(int)(object)enumValue})", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработка примитивных типов
|
|
||||||
if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal))
|
|
||||||
{
|
|
||||||
return (value.ToString() ?? "null", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработка IEntity
|
|
||||||
if (typeof(IEntity).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
var entity = (IEntity)value;
|
|
||||||
var entityType = entity.Entity.Type;
|
|
||||||
var typeName = PgNameHelper.GetPgName(entityType);
|
|
||||||
var slug = entity.Entity.Slug;
|
|
||||||
var name = entity.Entity.Name[LocaleType.English];
|
|
||||||
return ($"<a href=\"/{typeName}/{slug}\">{name}</a>", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработка коллекций
|
|
||||||
if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string))
|
|
||||||
{
|
|
||||||
var collection = (IEnumerable)value;
|
|
||||||
var items = new List<string>();
|
|
||||||
bool allImplemented = true;
|
|
||||||
|
|
||||||
foreach (var item in collection)
|
|
||||||
{
|
|
||||||
var (itemDisplay, itemImplemented) = FormatValue(item);
|
|
||||||
items.Add(itemDisplay);
|
|
||||||
if (!itemImplemented)
|
|
||||||
allImplemented = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.Count == 0)
|
|
||||||
return ("(empty)", true);
|
|
||||||
|
|
||||||
return (string.Join(", ", items), allImplemented);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Для остальных сложных объектов
|
|
||||||
return ($"[{type.Name}] - НЕ РЕАЛИЗОВАНО", false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using NpgsqlTypes;
|
using NpgsqlTypes;
|
||||||
using Pf2e.Database.Model.Enum;
|
using Pf2e.Database.Model.Enum;
|
||||||
|
|
||||||
namespace Pf2e.Web.Helpers
|
namespace Pf2e.Web.Helpers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Âñïîìîãàòåëüíûé êëàññ äëÿ ïîëó÷åíèÿ èìåí PostgresSQL èç ïåðå÷èñëåíèé.
|
||||||
|
/// </summary>
|
||||||
public static class PgNameHelper
|
public static class PgNameHelper
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ïîëó÷àåò èìÿ PostgresSQL äëÿ çàäàííîãî òèïà ñóùíîñòè.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityType">Òèï ñóùíîñòè.</param>
|
||||||
|
/// <returns>Ñòðîêîâîå ïðåäñòàâëåíèå èìåíè PostgresSQL.</returns>
|
||||||
public static string GetPgName(EntityType entityType)
|
public static string GetPgName(EntityType entityType)
|
||||||
{
|
{
|
||||||
var fieldInfo = entityType.GetType().GetField(entityType.ToString());
|
FieldInfo? fieldInfo = entityType.GetType().GetField(entityType.ToString());
|
||||||
var pgNameAttribute = fieldInfo?.GetCustomAttribute<PgNameAttribute>();
|
PgNameAttribute? pgNameAttribute = fieldInfo?.GetCustomAttribute<PgNameAttribute>();
|
||||||
return pgNameAttribute?.PgName ?? entityType.ToString().ToLower();
|
return pgNameAttribute?.PgName ?? entityType.ToString().ToLower();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@page "{type}/{slug}"
|
@page "/{type}/{slug}"
|
||||||
@model Pf2e.Web.Pages.EntityModel
|
@model Pf2e.Web.Pages.EntityModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
@@ -17,18 +17,16 @@
|
|||||||
<dl class="row">
|
<dl class="row">
|
||||||
@foreach (var field in group.Fields)
|
@foreach (var field in group.Fields)
|
||||||
{
|
{
|
||||||
<dt class="col-sm-3">@field.Name</dt>
|
<dt class="col-sm-3">
|
||||||
|
@field.Name
|
||||||
|
<span class="text-muted">[@field.Type]</span>
|
||||||
|
</dt>
|
||||||
<dd class="col-sm-9">
|
<dd class="col-sm-9">
|
||||||
<span class="text-muted">[@field.Type]</span>
|
@if (!field.IsImplemented)
|
||||||
@if (field.IsImplemented)
|
|
||||||
{
|
|
||||||
@Html.Raw(field.DisplayValue)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
<span class="badge bg-warning text-dark">НЕ РЕАЛИЗОВАНО:</span>
|
<span class="badge bg-warning text-dark">НЕ РЕАЛИЗОВАНО:</span>
|
||||||
@Html.Raw(field.DisplayValue)
|
|
||||||
}
|
}
|
||||||
|
@Html.Raw(field.DisplayValue)
|
||||||
</dd>
|
</dd>
|
||||||
}
|
}
|
||||||
</dl>
|
</dl>
|
||||||
|
|||||||
@@ -26,39 +26,65 @@ namespace Pf2e.Web.Pages
|
|||||||
public IActionResult OnGet(string type, string slug)
|
public IActionResult OnGet(string type, string slug)
|
||||||
{
|
{
|
||||||
// Добавим отладочную информацию
|
// Добавим отладочную информацию
|
||||||
System.Diagnostics.Debug.WriteLine($"EntityModel.OnGet called with type: {type}, slug: {slug}");
|
System.Diagnostics.Debug.WriteLine(
|
||||||
|
$"EntityModel.OnGet called with type: {type}, slug: {slug}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Логируем в консоль для веб-сервера
|
||||||
|
Console.WriteLine($"EntityModel.OnGet called with type: {type}, slug: {slug}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Получаем тип сущности
|
// Получаем тип сущности
|
||||||
EntityType entityType = EntityTypeModel.Enum<EntityType>(type);
|
EntityType entityType = EntityTypeModel.Enum<EntityType>(type);
|
||||||
System.Diagnostics.Debug.WriteLine($"EntityType resolved to: {entityType}");
|
System.Diagnostics.Debug.WriteLine($"EntityType resolved to: {entityType}");
|
||||||
|
Console.WriteLine($"EntityType resolved to: {entityType}");
|
||||||
|
|
||||||
// Находим сущность по slug и типу
|
// Находим сущность по slug и типу
|
||||||
Entity? entity = _context.Entities.FirstOrDefault(e =>
|
Entity? entity = _context
|
||||||
e.Slug.ToLower() == slug.ToLower() && e.Type == entityType
|
.Entities.AsNoTracking()
|
||||||
);
|
.FirstOrDefault(e =>
|
||||||
|
e.Slug.ToLower() == slug.ToLower() && e.Type == entityType
|
||||||
|
);
|
||||||
|
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"Entity not found: type={entityType}, slug={slug}");
|
System.Diagnostics.Debug.WriteLine(
|
||||||
|
$"Entity not found: type={entityType}, slug={slug}"
|
||||||
|
);
|
||||||
|
Console.WriteLine($"Entity not found: type={entityType}, slug={slug}");
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"Entity found: {entity.Id} - {entity.Name[Pf2e.Database.Model.Common.I18n.LocaleType.English]}");
|
System.Diagnostics.Debug.WriteLine(
|
||||||
|
$"Entity found: {entity.Id} - {entity.Name[Pf2e.Database.Model.Common.I18n.LocaleType.English]}"
|
||||||
|
);
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Entity found: {entity.Id} - {entity.Name[Pf2e.Database.Model.Common.I18n.LocaleType.English]}"
|
||||||
|
);
|
||||||
|
|
||||||
// Создаём FullEntity
|
// Создаём FullEntity
|
||||||
FullEntity = new(_context, entity);
|
FullEntity = new(_context, entityType, entity.Id);
|
||||||
|
|
||||||
// Сериализуем для отображения
|
// Сериализуем для отображения
|
||||||
EntitySerializer serializer = new();
|
EntitySerializer serializer = new();
|
||||||
SerializationResult = serializer.Serialize(FullEntity);
|
SerializationResult = serializer.Serialize(FullEntity);
|
||||||
|
|
||||||
|
// Set breadcrumbs
|
||||||
|
var typeName = Pf2e.Web.Helpers.PgNameHelper.GetPgName(entityType);
|
||||||
|
ViewData["Breadcrumbs"] = new List<(string, string)>
|
||||||
|
{
|
||||||
|
("Home", "/"),
|
||||||
|
(entityType.ToString(), $"/{typeName}"),
|
||||||
|
(entity.Name[Pf2e.Database.Model.Common.I18n.LocaleType.English], "")
|
||||||
|
};
|
||||||
|
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"Error in EntityModel.OnGet: {ex.Message}");
|
System.Diagnostics.Debug.WriteLine($"Error in EntityModel.OnGet: {ex.Message}");
|
||||||
|
Console.WriteLine($"Error in EntityModel.OnGet: {ex.Message}");
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,24 +10,15 @@ using Pf2e.Database;
|
|||||||
using Pf2e.Database.Model.Base;
|
using Pf2e.Database.Model.Base;
|
||||||
using Pf2e.Database.Model.Enum;
|
using Pf2e.Database.Model.Enum;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
||||||
|
|
||||||
namespace Pf2e.Web.Pages
|
namespace Pf2e.Web.Pages
|
||||||
{
|
{
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||||
public class EntityTypeModel : PageModel
|
public class EntityTypeModel(Pf2eDbContext context) : PageModel
|
||||||
{
|
{
|
||||||
private readonly Pf2eDbContext _context;
|
|
||||||
|
|
||||||
public EntityTypeModel(Pf2eDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BindProperty(SupportsGet = true)]
|
[BindProperty(SupportsGet = true)]
|
||||||
public int? PageNum { get; set; } = 1;
|
public int? PageNum { get; set; } = 1;
|
||||||
|
|
||||||
public IList<Entity> Entity { get; set; } = default!;
|
public IList<Entity> Entity { get; set; } = null!;
|
||||||
|
|
||||||
public EntityType CurrentType;
|
public EntityType CurrentType;
|
||||||
|
|
||||||
@@ -41,7 +32,7 @@ namespace Pf2e.Web.Pages
|
|||||||
{
|
{
|
||||||
// Добавим отладочную информацию
|
// Добавим отладочную информацию
|
||||||
System.Diagnostics.Debug.WriteLine($"EntityTypeModel.OnGet called with type: {type}");
|
System.Diagnostics.Debug.WriteLine($"EntityTypeModel.OnGet called with type: {type}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CurrentType = Enum<EntityType>(type);
|
CurrentType = Enum<EntityType>(type);
|
||||||
@@ -53,18 +44,26 @@ namespace Pf2e.Web.Pages
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = _context.Entities.Count(entity => entity.Type == CurrentType);
|
int count = context.Entities.Count(entity => entity.Type == CurrentType);
|
||||||
System.Diagnostics.Debug.WriteLine($"Count for {CurrentType}: {count}");
|
System.Diagnostics.Debug.WriteLine($"Count for {CurrentType}: {count}");
|
||||||
|
|
||||||
LastPage = (int)Math.Ceiling(count / (double)PerPage);
|
LastPage = (int)Math.Ceiling(count / (double)PerPage);
|
||||||
|
|
||||||
Entity = _context
|
Entity = context
|
||||||
.Entities.Where(entity => entity.Type == CurrentType)
|
.Entities.Where(entity => entity.Type == CurrentType)
|
||||||
.Skip((CurrentPage - 1) * PerPage)
|
.Skip((CurrentPage - 1) * PerPage)
|
||||||
.Take(PerPage)
|
.Take(PerPage)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"Returning page with {Entity.Count} entities");
|
System.Diagnostics.Debug.WriteLine($"Returning page with {Entity.Count} entities");
|
||||||
|
|
||||||
|
// Set breadcrumbs
|
||||||
|
ViewData["Breadcrumbs"] = new List<(string, string)>
|
||||||
|
{
|
||||||
|
("Home", "/"),
|
||||||
|
(CurrentType.ToString(), ""),
|
||||||
|
};
|
||||||
|
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +71,14 @@ namespace Pf2e.Web.Pages
|
|||||||
/// Преобразование значения из JSON в перечисление.
|
/// Преобразование значения из JSON в перечисление.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Значение, которое нужно преобразовать</param>
|
/// <param name="value">Значение, которое нужно преобразовать</param>
|
||||||
/// <returns>Объект типа `T`, если преобразование успешно, иначе выбрасывает исключение</returns>
|
/// <returns>Объект типа <c>T</c>, если преобразование успешно, иначе выбрасывает исключение</returns>
|
||||||
public static T Enum<T>(string value)
|
public static T Enum<T>(string value)
|
||||||
where T : Enum
|
where T : Enum
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"Trying to resolve enum value: {value} for type {typeof(T)}");
|
System.Diagnostics.Debug.WriteLine(
|
||||||
|
$"Trying to resolve enum value: {value} for type {typeof(T)}"
|
||||||
|
);
|
||||||
|
|
||||||
foreach (System.Reflection.FieldInfo field in typeof(T).GetFields())
|
foreach (System.Reflection.FieldInfo field in typeof(T).GetFields())
|
||||||
{
|
{
|
||||||
List<string> variants = [field.Name];
|
List<string> variants = [field.Name];
|
||||||
@@ -90,8 +91,10 @@ namespace Pf2e.Web.Pages
|
|||||||
variants.Add(pgAttribute.PgName);
|
variants.Add(pgAttribute.PgName);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"Checking field: {field.Name}, variants: {string.Join(", ", variants)}");
|
System.Diagnostics.Debug.WriteLine(
|
||||||
|
$"Checking field: {field.Name}, variants: {string.Join(", ", variants)}"
|
||||||
|
);
|
||||||
|
|
||||||
if (variants.Contains(value))
|
if (variants.Contains(value))
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"Matched field: {field.Name}");
|
System.Diagnostics.Debug.WriteLine($"Matched field: {field.Name}");
|
||||||
|
|||||||
@@ -24,5 +24,10 @@ public class IndexModel : PageModel
|
|||||||
Data = Context
|
Data = Context
|
||||||
.Entities.GroupBy(e => e.Type)
|
.Entities.GroupBy(e => e.Type)
|
||||||
.ToDictionary(entities => entities.Key, entities => entities.Count());
|
.ToDictionary(entities => entities.Key, entities => entities.Count());
|
||||||
|
|
||||||
|
ViewData["Breadcrumbs"] = new List<(string, string)>
|
||||||
|
{
|
||||||
|
("Home", "")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ public class PrivacyModel : PageModel
|
|||||||
|
|
||||||
public void OnGet()
|
public void OnGet()
|
||||||
{
|
{
|
||||||
|
ViewData["Breadcrumbs"] = new List<(string, string)>
|
||||||
|
{
|
||||||
|
("Home", "/"),
|
||||||
|
("Privacy Policy", "")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,28 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
@if (ViewData["Breadcrumbs"] != null)
|
||||||
|
{
|
||||||
|
@foreach (var crumb in (List<(string, string)>)ViewData["Breadcrumbs"]!)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(crumb.Item2))
|
||||||
|
{
|
||||||
|
<li class="breadcrumb-item"><a href="@crumb.Item2">@crumb.Item1</a></li>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">@crumb.Item1</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Home</li>
|
||||||
|
}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
<main role="main" class="pb-3">
|
<main role="main" class="pb-3">
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="dotenv.net" Version="4.0.0" />
|
<PackageReference Include="dotenv.net" Version="4.0.0" />
|
||||||
|
<PackageReference Include="Markdig" Version="0.42.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|||||||
+2
-2
@@ -13,12 +13,12 @@ builder.Services.AddDbContext<Pf2eDbContext>(options =>
|
|||||||
options.UseNpgsql(EnvReader.GetStringValue("DATABASE_CONNECTION"))
|
options.UseNpgsql(EnvReader.GetStringValue("DATABASE_CONNECTION"))
|
||||||
);
|
);
|
||||||
|
|
||||||
var app = builder.Build();
|
WebApplication app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error");
|
//app.UseExceptionHandler("/Error");
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user