From a7e5c11007464d14f1ea10d3e520cf7ec852bbc1 Mon Sep 17 00:00:00 2001 From: zpc Date: Mon, 16 Mar 2026 22:59:37 +0800 Subject: [PATCH] 21 --- .../Data/AdminBusinessDbContext.cs | 19 ++ .../Entities/ReportPageConfig.cs | 68 ++++++ .../Models/Common/ErrorCodes.cs | 14 ++ .../ReportPageConfig/ReportPageConfigDto.cs | 135 ++++++++++++ .../Interfaces/IReportPageConfigService.cs | 40 ++++ .../Services/ReportPageConfigService.cs | 207 ++++++++++++++++++ .../Pages/Report/FullReport.cshtml | 75 +++++++ .../Pages/Report/FullReport.cshtml.cs | 68 ++++++ .../wwwroot/css/pages/full-report.css | 84 +++++++ 9 files changed, 710 insertions(+) create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Entities/ReportPageConfig.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Models/ReportPageConfig/ReportPageConfigDto.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IReportPageConfigService.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Services/ReportPageConfigService.cs create mode 100644 server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml create mode 100644 server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml.cs create mode 100644 server/MiAssessment/src/MiAssessment.Api/wwwroot/css/pages/full-report.css diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs index d1ef6fa..3986280 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs @@ -155,6 +155,15 @@ public class AdminBusinessDbContext : DbContext #endregion + #region 报告页面配置 + + /// + /// 报告页面配置表 + /// + public DbSet ReportPageConfigs { get; set; } = null!; + + #endregion + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); @@ -398,5 +407,15 @@ public class AdminBusinessDbContext : DbContext entity.HasIndex(e => e.Status) .HasDatabaseName("IX_business_pages_status"); }); + + // ============================================= + // ReportPageConfig 配置 + // ============================================= + modelBuilder.Entity(entity => + { + // SortOrder 索引 + entity.HasIndex(e => e.SortOrder) + .HasDatabaseName("ix_rpc_sort_order"); + }); } } diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/ReportPageConfig.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/ReportPageConfig.cs new file mode 100644 index 0000000..2502dc1 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/ReportPageConfig.cs @@ -0,0 +1,68 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace MiAssessment.Admin.Business.Entities; + +/// +/// 报告页面配置表 +/// +[Table("report_page_configs")] +public class ReportPageConfig +{ + /// + /// 主键ID + /// + [Key] + public long Id { get; set; } + + /// + /// 页面类型:1静态图片 2网页截图 + /// + public int PageType { get; set; } + + /// + /// 页面标识名称 + /// + [Required] + [MaxLength(50)] + public string PageName { get; set; } = null!; + + /// + /// 页面显示标题 + /// + [Required] + [MaxLength(100)] + public string Title { get; set; } = null!; + + /// + /// 排序序号 + /// + public int SortOrder { get; set; } + + /// + /// 静态图片路径(PageType=1 时使用) + /// + [MaxLength(500)] + public string? ImageUrl { get; set; } + + /// + /// 网页路由路径(PageType=2 时使用) + /// + [MaxLength(200)] + public string? RouteUrl { get; set; } + + /// + /// 状态:0禁用 1启用 + /// + public int Status { get; set; } = 1; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Common/ErrorCodes.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Common/ErrorCodes.cs index 4161451..369a68b 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Common/ErrorCodes.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Common/ErrorCodes.cs @@ -265,6 +265,20 @@ public static class ErrorCodes #endregion + #region 报告页面配置模块错误 (3800-3899) + + /// + /// 报告页面配置不存在 + /// + public const int ReportPageConfigNotFound = 3801; + + /// + /// 页面标识名称已存在 + /// + public const int ReportPageNameExists = 3802; + + #endregion + #region 系统错误 (5000-5999) /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/ReportPageConfig/ReportPageConfigDto.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/ReportPageConfig/ReportPageConfigDto.cs new file mode 100644 index 0000000..f56644e --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/ReportPageConfig/ReportPageConfigDto.cs @@ -0,0 +1,135 @@ +namespace MiAssessment.Admin.Business.Models.ReportPageConfig; + +/// +/// 报告页面配置 DTO +/// +public class ReportPageConfigDto +{ + /// + /// 主键ID + /// + public long Id { get; set; } + + /// + /// 页面类型:1静态图片 2网页截图 + /// + public int PageType { get; set; } + + /// + /// 页面类型名称 + /// + public string PageTypeName { get; set; } = ""; + + /// + /// 页面标识名称 + /// + public string PageName { get; set; } = null!; + + /// + /// 页面显示标题 + /// + public string Title { get; set; } = null!; + + /// + /// 排序序号 + /// + public int SortOrder { get; set; } + + /// + /// 静态图片路径 + /// + public string? ImageUrl { get; set; } + + /// + /// 网页路由路径 + /// + public string? RouteUrl { get; set; } + + /// + /// 状态:0禁用 1启用 + /// + public int Status { get; set; } + + /// + /// 状态名称 + /// + public string StatusName { get; set; } = ""; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } +} + +/// +/// 报告页面配置查询请求 +/// +public class ReportPageConfigQueryRequest : PagedRequest +{ + /// + /// 页面类型筛选 + /// + public int? PageType { get; set; } + + /// + /// 状态筛选 + /// + public int? Status { get; set; } + + /// + /// 标题关键字 + /// + public string? Title { get; set; } +} + +/// +/// 创建报告页面配置请求 +/// +public class CreateReportPageConfigRequest +{ + /// + /// 页面类型:1静态图片 2网页截图 + /// + public int PageType { get; set; } + + /// + /// 页面标识名称 + /// + public string PageName { get; set; } = null!; + + /// + /// 页面显示标题 + /// + public string Title { get; set; } = null!; + + /// + /// 排序序号 + /// + public int SortOrder { get; set; } + + /// + /// 静态图片路径 + /// + public string? ImageUrl { get; set; } + + /// + /// 网页路由路径 + /// + public string? RouteUrl { get; set; } + + /// + /// 状态:0禁用 1启用 + /// + public int Status { get; set; } = 1; +} + +/// +/// 更新报告页面配置请求 +/// +public class UpdateReportPageConfigRequest : CreateReportPageConfigRequest +{ + /// + /// 主键ID + /// + public long Id { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IReportPageConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IReportPageConfigService.cs new file mode 100644 index 0000000..e72c17a --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IReportPageConfigService.cs @@ -0,0 +1,40 @@ +using MiAssessment.Admin.Business.Models; +using MiAssessment.Admin.Business.Models.ReportPageConfig; + +namespace MiAssessment.Admin.Business.Services.Interfaces; + +/// +/// 报告页面配置服务接口 +/// +public interface IReportPageConfigService +{ + /// + /// 获取报告页面配置列表 + /// + Task> GetListAsync(ReportPageConfigQueryRequest request); + + /// + /// 创建报告页面配置 + /// + Task CreateAsync(CreateReportPageConfigRequest request); + + /// + /// 更新报告页面配置 + /// + Task UpdateAsync(UpdateReportPageConfigRequest request); + + /// + /// 删除报告页面配置 + /// + Task DeleteAsync(long id); + + /// + /// 更新状态 + /// + Task UpdateStatusAsync(long id, int status); + + /// + /// 批量更新排序 + /// + Task UpdateSortAsync(List items); +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ReportPageConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ReportPageConfigService.cs new file mode 100644 index 0000000..efb2c38 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ReportPageConfigService.cs @@ -0,0 +1,207 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using MiAssessment.Admin.Business.Data; +using MiAssessment.Admin.Business.Entities; +using MiAssessment.Admin.Business.Models; +using MiAssessment.Admin.Business.Models.Common; +using MiAssessment.Admin.Business.Models.ReportPageConfig; +using MiAssessment.Admin.Business.Services.Interfaces; + +namespace MiAssessment.Admin.Business.Services; + +/// +/// 报告页面配置服务实现 +/// +public class ReportPageConfigService : IReportPageConfigService +{ + private readonly AdminBusinessDbContext _dbContext; + private readonly ILogger _logger; + + private static readonly Dictionary PageTypeNames = new() + { + { 1, "静态图片" }, + { 2, "网页截图" } + }; + + private static readonly Dictionary StatusNames = new() + { + { 0, "禁用" }, + { 1, "启用" } + }; + + public ReportPageConfigService( + AdminBusinessDbContext dbContext, + ILogger logger) + { + _dbContext = dbContext; + _logger = logger; + } + + /// + public async Task> GetListAsync(ReportPageConfigQueryRequest request) + { + var query = _dbContext.ReportPageConfigs.AsNoTracking().AsQueryable(); + + if (request.PageType.HasValue) + query = query.Where(p => p.PageType == request.PageType.Value); + + if (request.Status.HasValue) + query = query.Where(p => p.Status == request.Status.Value); + + if (!string.IsNullOrWhiteSpace(request.Title)) + query = query.Where(p => p.Title.Contains(request.Title)); + + var total = await query.CountAsync(); + + var items = await query + .OrderBy(p => p.SortOrder) + .Skip(request.Skip) + .Take(request.PageSize) + .Select(p => new ReportPageConfigDto + { + Id = p.Id, + PageType = p.PageType, + PageName = p.PageName, + Title = p.Title, + SortOrder = p.SortOrder, + ImageUrl = p.ImageUrl, + RouteUrl = p.RouteUrl, + Status = p.Status, + CreateTime = p.CreateTime + }) + .ToListAsync(); + + // 内存中映射名称 + foreach (var item in items) + { + item.PageTypeName = PageTypeNames.TryGetValue(item.PageType, out var pn) ? pn : "未知"; + item.StatusName = StatusNames.TryGetValue(item.Status, out var sn) ? sn : "未知"; + } + + return PagedResult.Create(items, total, request.Page, request.PageSize); + } + + /// + public async Task CreateAsync(CreateReportPageConfigRequest request) + { + if (string.IsNullOrWhiteSpace(request.PageName)) + throw new BusinessException(ErrorCodes.ParamError, "页面标识不能为空"); + + if (string.IsNullOrWhiteSpace(request.Title)) + throw new BusinessException(ErrorCodes.ParamError, "页面标题不能为空"); + + // 检查 PageName 唯一性 + var exists = await _dbContext.ReportPageConfigs + .AnyAsync(p => p.PageName == request.PageName); + if (exists) + throw new BusinessException(ErrorCodes.ReportPageNameExists, "页面标识已存在"); + + var entity = new ReportPageConfig + { + PageType = request.PageType, + PageName = request.PageName, + Title = request.Title, + SortOrder = request.SortOrder, + ImageUrl = request.ImageUrl, + RouteUrl = request.RouteUrl, + Status = request.Status, + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now + }; + + _dbContext.ReportPageConfigs.Add(entity); + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("创建报告页面配置成功,ID: {Id}, 标题: {Title}", entity.Id, entity.Title); + return entity.Id; + } + + /// + public async Task UpdateAsync(UpdateReportPageConfigRequest request) + { + var entity = await _dbContext.ReportPageConfigs.FindAsync(request.Id); + if (entity == null) + throw new BusinessException(ErrorCodes.ReportPageConfigNotFound, "报告页面配置不存在"); + + if (string.IsNullOrWhiteSpace(request.PageName)) + throw new BusinessException(ErrorCodes.ParamError, "页面标识不能为空"); + + if (string.IsNullOrWhiteSpace(request.Title)) + throw new BusinessException(ErrorCodes.ParamError, "页面标题不能为空"); + + // 检查 PageName 唯一性(排除自身) + var exists = await _dbContext.ReportPageConfigs + .AnyAsync(p => p.PageName == request.PageName && p.Id != request.Id); + if (exists) + throw new BusinessException(ErrorCodes.ReportPageNameExists, "页面标识已存在"); + + entity.PageType = request.PageType; + entity.PageName = request.PageName; + entity.Title = request.Title; + entity.SortOrder = request.SortOrder; + entity.ImageUrl = request.ImageUrl; + entity.RouteUrl = request.RouteUrl; + entity.Status = request.Status; + entity.UpdateTime = DateTime.Now; + + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("更新报告页面配置成功,ID: {Id}", entity.Id); + return true; + } + + /// + public async Task DeleteAsync(long id) + { + var entity = await _dbContext.ReportPageConfigs.FindAsync(id); + if (entity == null) + throw new BusinessException(ErrorCodes.ReportPageConfigNotFound, "报告页面配置不存在"); + + _dbContext.ReportPageConfigs.Remove(entity); + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("删除报告页面配置成功,ID: {Id}", id); + return true; + } + + /// + public async Task UpdateStatusAsync(long id, int status) + { + var entity = await _dbContext.ReportPageConfigs.FindAsync(id); + if (entity == null) + throw new BusinessException(ErrorCodes.ReportPageConfigNotFound, "报告页面配置不存在"); + + entity.Status = status; + entity.UpdateTime = DateTime.Now; + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("更新报告页面配置状态成功,ID: {Id}, 状态: {Status}", id, status); + return true; + } + + /// + public async Task UpdateSortAsync(List items) + { + if (items == null || items.Count == 0) return true; + + var ids = items.Select(i => i.Id).ToList(); + var entities = await _dbContext.ReportPageConfigs + .Where(p => ids.Contains(p.Id)) + .ToListAsync(); + + foreach (var item in items) + { + var entity = entities.FirstOrDefault(e => e.Id == item.Id); + if (entity != null) + { + entity.SortOrder = item.Sort; + entity.UpdateTime = DateTime.Now; + } + } + + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("批量更新报告页面排序成功,更新数量: {Count}", items.Count); + return true; + } +} diff --git a/server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml b/server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml new file mode 100644 index 0000000..b6775d6 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml @@ -0,0 +1,75 @@ +@page "/report/full" +@model MiAssessment.Api.Pages.Report.FullReportModel +@{ + ViewData["Title"] = "测评报告完整预览"; + Layout = null; +} + + + + + + + 测评报告完整预览 + + + + @if (!string.IsNullOrEmpty(Model.ErrorMessage)) + { +
+

@Model.ErrorMessage

+
+ } + else + { +
+ + +
+ @foreach (var item in Model.Pages) + { +
+
+ +
+ +
+ } +
+
+ } + + + + diff --git a/server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml.cs b/server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml.cs new file mode 100644 index 0000000..a89e9ba --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Api/Pages/Report/FullReport.cshtml.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; +using MiAssessment.Model.Data; +using MiAssessment.Model.Entities; + +namespace MiAssessment.Api.Pages.Report; + +/// +/// 报告完整预览页 PageModel +/// 路由:/report/full?recordId=3 +/// 读取 report_page_configs 表,按 SortOrder 顺序展示所有报告页面 +/// +public class FullReportModel : PageModel +{ + private readonly MiAssessmentDbContext _dbContext; + + /// + /// 测评记录ID + /// + public long RecordId { get; set; } + + /// + /// 所有启用的报告页面配置(按 SortOrder 排序) + /// + public List Pages { get; set; } = new(); + + /// + /// 错误信息 + /// + public string? ErrorMessage { get; set; } + + public FullReportModel(MiAssessmentDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task OnGetAsync(long? recordId) + { + if (recordId == null || recordId <= 0) + { + ErrorMessage = "缺少测评记录参数"; + return Page(); + } + + RecordId = recordId.Value; + + // 只加载网页截图页(PageType=2),静态图片页有专门的网页不需要在此展示 + Pages = await _dbContext.ReportPageConfigs + .AsNoTracking() + .Where(p => p.Status == 1 && p.PageType == 2) + .OrderBy(p => p.SortOrder) + .ToListAsync(); + + return Page(); + } + + /// + /// 构建 iframe 的完整 URL(自动拼接 recordId) + /// + public string BuildPageUrl(string routeUrl) + { + if (string.IsNullOrEmpty(routeUrl)) return ""; + // 如果已有查询参数,用 & 拼接;否则用 ? + var separator = routeUrl.Contains('?') ? "&" : "?"; + return $"{routeUrl}{separator}recordId={RecordId}"; + } +} diff --git a/server/MiAssessment/src/MiAssessment.Api/wwwroot/css/pages/full-report.css b/server/MiAssessment/src/MiAssessment.Api/wwwroot/css/pages/full-report.css new file mode 100644 index 0000000..8d1eff1 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Api/wwwroot/css/pages/full-report.css @@ -0,0 +1,84 @@ +/* 报告完整预览页样式 */ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: #e8e8e8; + font-family: "Microsoft YaHei", "PingFang SC", sans-serif; + min-height: 100vh; +} + +/* 错误提示 */ +.full-error { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + color: #ff4d4f; + font-size: 18px; +} + +/* 外层容器 */ +.full-container { + max-width: 1400px; + margin: 0 auto; + padding: 20px 0 60px; +} + +/* 顶部信息栏 */ +.full-header { + display: flex; + justify-content: center; + align-items: center; + gap: 16px; + padding: 16px 0 24px; +} + +.full-header-title { + font-size: 20px; + font-weight: 600; + color: #333; +} + +.full-header-count { + font-size: 14px; + color: #999; +} + +/* 页面列表 */ +.full-pages { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; +} + +/* 单个页面包装 */ +.full-page-wrapper { + display: flex; + flex-direction: column; + align-items: center; +} + +/* 页面框架(固定 1309×926) */ +.full-page-frame { + width: 1309px; + height: 926px; + background-color: #fff; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); + overflow: hidden; + position: relative; +} + +/* iframe */ +.full-page-iframe { + width: 1309px; + height: 926px; + border: none; + display: block; +} +