This commit is contained in:
zpc 2026-03-16 00:11:18 +08:00
parent 3fc5433e0c
commit 0c63c20201
4 changed files with 337 additions and 0 deletions

View File

@ -12,6 +12,7 @@
| 6 | 较弱智能详情② | `/report/weaker-intelligence?recordId=3&rank=2` | 网页截图 | 绿色模板 rank=2/倒数第2结论文本 | ✅ 已完成 |
| 7 | 个人特质分析 | `/report/personality-traits` | 网页截图 | CategoryType=2 结果+结论 | ⬜ 骨架 |
| 8 | 40项细分能力分析 | `/report/sub-abilities?recordId=3` | 网页截图 | CategoryType=3 雷达图40项分数 | ✅ 已完成 |
| 8.1 | 细分能力排序TOP10 | `/report/sub-abilities-ranking?recordId=3` | 网页截图 | CategoryType=3 最强/偏弱TOP10柱状图+排名 | ✅ 已完成 |
| 9 | 先天学习类型分析 | `/report/learning-types` | 网页截图 | CategoryType=4 结果+结论 | ⬜ 骨架 |
| 10 | 学习关键能力分析 | `/report/learning-abilities` | 网页截图 | CategoryType=5 结果+结论 | ⬜ 骨架 |
| 11 | 科学大脑类型分析 | `/report/brain-types` | 网页截图 | CategoryType=6 结果+结论 | ⬜ 骨架 |

View File

@ -0,0 +1,178 @@
@page "/report/sub-abilities-ranking"
@model MiAssessment.Api.Pages.Report.SubAbilitiesRankingModel
@{
ViewData["Title"] = "细分能力排序";
ViewData["PageTitle"] = null;
ViewData["PageNumber"] = null;
}
@if (!Model.IsSuccess)
{
<div class="report-error" data-render-error="true">
<p>@Model.ErrorMessage</p>
</div>
}
else
{
<div class="sar-page">
<!-- 上半部分:两个横向柱状图 -->
<div class="sar-charts">
<!-- 左侧:最强排序 TOP10 -->
<div class="sar-chart-panel">
<div class="sar-chart-title sar-title-strong">细分能力最强排序--TOP10</div>
<canvas id="strongChart" width="540" height="500"></canvas>
</div>
<!-- 右侧:偏弱排序 TOP10 -->
<div class="sar-chart-panel">
<div class="sar-chart-title sar-title-weak">细分能力偏弱排序--TOP10</div>
<canvas id="weakChart" width="540" height="500"></canvas>
</div>
</div>
<!-- 下半部分:排名文字列表 -->
<div class="sar-rankings">
<!-- 最强细分能力排名 -->
<div class="sar-rank-card sar-rank-strong">
<div class="sar-rank-badge sar-badge-strong">最强细分能力排名</div>
<div class="sar-rank-list">
@for (var i = 0; i < Model.TopStrongest.Count; i++)
{
<span class="sar-rank-item">@Model.TopStrongest[i].CategoryName</span>
@if (i < Model.TopStrongest.Count - 1)
{
<span class="sar-rank-sep">、</span>
}
else
{
<span class="sar-rank-sep">……</span>
}
}
</div>
</div>
<!-- 较弱细分能力排名 -->
<div class="sar-rank-card sar-rank-weak">
<div class="sar-rank-badge sar-badge-weak">较弱细分能力排名</div>
<div class="sar-rank-list">
@for (var i = 0; i < Model.TopWeakest.Count; i++)
{
<span class="sar-rank-item">@Model.TopWeakest[i].CategoryName</span>
@if (i < Model.TopWeakest.Count - 1)
{
<span class="sar-rank-sep">、</span>
}
else
{
<span class="sar-rank-sep">……</span>
}
}
</div>
</div>
</div>
</div>
}
@section Styles {
<link rel="stylesheet" href="/css/pages/sub-abilities-ranking.css" />
}
@section Scripts {
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<script>
window.__deferRenderComplete = true;
// 最强 TOP10 数据
var strongLabels = [@Html.Raw(string.Join(",", Model.TopStrongest.Select(x => $"\"{x.CategoryName}\"")))];
var strongScores = [@Html.Raw(string.Join(",", Model.TopStrongest.Select(x => x.Score.ToString("F0"))))];
// 偏弱 TOP10 数据
var weakLabels = [@Html.Raw(string.Join(",", Model.TopWeakest.Select(x => $"\"{x.CategoryName}\"")))];
var weakScores = [@Html.Raw(string.Join(",", Model.TopWeakest.Select(x => x.Score.ToString("F0"))))];
// 最强分数的最大值(用于坐标轴)
var strongMax = Math.ceil(Math.max(...strongScores) / 5) * 5 + 5;
var weakMax = Math.ceil(Math.max(...weakScores) / 2) * 2 + 2;
// 数据点标签插件
var dataLabelPlugin = {
id: 'barDataLabels',
afterDatasetsDraw: function(chart) {
var ctx = chart.ctx;
var meta = chart.getDatasetMeta(0);
var dataset = chart.data.datasets[0];
ctx.font = 'bold 12px "Microsoft YaHei", sans-serif';
ctx.textBaseline = 'middle';
meta.data.forEach(function(bar, i) {
var val = dataset.data[i];
ctx.fillStyle = dataset.borderColor;
ctx.textAlign = 'left';
ctx.fillText(val, bar.x + 4, bar.y);
});
}
};
// 公共配置
function createBarConfig(labels, scores, maxVal, barColor, borderColor) {
return {
type: 'bar',
data: {
labels: labels,
datasets: [{
data: scores,
backgroundColor: barColor,
borderColor: borderColor,
borderWidth: 0,
borderRadius: 2,
barThickness: 24
}]
},
options: {
indexAxis: 'y',
responsive: false,
plugins: { legend: { display: false } },
scales: {
x: {
beginAtZero: true,
max: maxVal,
ticks: {
font: { size: 11 },
color: '#999'
},
grid: {
color: 'rgba(0,0,0,0.05)'
}
},
y: {
ticks: {
font: { size: 12 },
color: '#666'
},
grid: { display: false }
}
},
layout: {
padding: { right: 30 }
}
},
plugins: [dataLabelPlugin]
};
}
// 渲染最强 TOP10 柱状图(蓝色)
var ctx1 = document.getElementById('strongChart').getContext('2d');
new Chart(ctx1, createBarConfig(
strongLabels, strongScores, strongMax,
'rgba(74, 144, 226, 0.6)', '#4A90E2'
));
// 渲染偏弱 TOP10 柱状图(红/粉色)
var ctx2 = document.getElementById('weakChart').getContext('2d');
new Chart(ctx2, createBarConfig(
weakLabels, weakScores, weakMax,
'rgba(230, 126, 115, 0.6)', '#E67E73'
));
document.body.setAttribute('data-render-complete', 'true');
</script>
}

View File

@ -0,0 +1,48 @@
using MiAssessment.Core.Interfaces;
using MiAssessment.Model.Models.Report;
namespace MiAssessment.Api.Pages.Report;
/// <summary>
/// 40项细分能力排序页 PageModel最强TOP10 + 偏弱TOP10
/// </summary>
public class SubAbilitiesRankingModel : ReportPageModelBase
{
/// <summary>
/// 最强细分能力 TOP10按分数降序
/// </summary>
public List<CategoryResultDataDto> TopStrongest { get; set; } = new();
/// <summary>
/// 偏弱细分能力 TOP10按分数升序
/// </summary>
public List<CategoryResultDataDto> TopWeakest { get; set; } = new();
public SubAbilitiesRankingModel(IReportDataService reportDataService)
: base(reportDataService)
{
}
protected override Task OnDataLoadedAsync()
{
if (ReportData?.ResultsByType != null &&
ReportData.ResultsByType.TryGetValue(3, out var items))
{
// 最强按分数降序取前10
TopStrongest = items
.OrderByDescending(x => x.Score)
.ThenBy(x => x.CategoryId)
.Take(10)
.ToList();
// 偏弱按分数升序取前10
TopWeakest = items
.OrderBy(x => x.Score)
.ThenBy(x => x.CategoryId)
.Take(10)
.ToList();
}
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,110 @@
/* ============================================
细分能力排序页样式最强/偏弱 TOP10
页面固定尺寸1309×926px
report-page padding: 40px 50px 可用 846px
============================================ */
.sar-page {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
justify-content: flex-start;
gap: 50px;
}
/* ---- 上半部分:两个柱状图并排 ---- */
.sar-charts {
display: flex;
gap: 50px;
justify-content: center;
}
.sar-chart-panel {
flex: 1;
max-width: 560px;
}
/* 图表标题 */
.sar-chart-title {
font-size: 20px;
font-weight: 700;
margin-bottom: 6px;
letter-spacing: 1px;
}
.sar-title-strong {
color: #4A90E2;
}
.sar-title-weak {
color: #E67E73;
}
/* ---- 下半部分:排名文字卡片 ---- */
.sar-rankings {
display: flex;
gap: 50px;
justify-content: center;
}
.sar-rank-card {
flex: 1;
max-width: 560px;
border: 2px solid;
border-radius: 12px;
padding: 28px 32px 22px;
position: relative;
}
.sar-rank-strong {
border-color: #E67E73;
}
.sar-rank-weak {
border-color: #52A06A;
}
/* 排名标签 badge */
.sar-rank-badge {
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
padding: 3px 24px;
border: 2px solid;
border-radius: 6px;
font-size: 17px;
font-weight: 700;
background: #fff;
white-space: nowrap;
}
.sar-badge-strong {
color: #E67E73;
border-color: #E67E73;
}
.sar-badge-weak {
color: #52A06A;
border-color: #52A06A;
}
/* 排名列表文字 */
.sar-rank-list {
font-size: 18px;
color: var(--text-color);
line-height: 2.4;
text-align: center;
padding-top: 8px;
}
.sar-rank-item {
display: inline;
}
.sar-rank-sep {
display: inline;
margin: 0 2px;
color: var(--text-secondary);
}