21
This commit is contained in:
parent
086e2c47bb
commit
2cdad00f86
|
|
@ -20,6 +20,7 @@
|
|||
| 10.1 | 学习关键能力结论 | `/report/learning-abilities-conclusion?recordId=3` | 网页截图 | CategoryType=5 最弱能力结论详情(全页文本卡片) | ✅ 已完成 |
|
||||
| 11 | 科学大脑类型分析 | `/report/brain-types?recordId=3` | 网页截图 | CategoryType=6 环形图+雷达图+竖向柱状图+结论 | ✅ 已完成 |
|
||||
| 12 | 性格类型分析 | `/report/character-types?recordId=3` | 网页截图 | CategoryType=7 环形图+雷达图+竖向柱状图+结论 | ✅ 已完成 |
|
||||
| 12.1 | 性格类型-图表页 | `/report/character-types-chart?recordId=3` | 网页截图 | CategoryType=7 雷达图+横向柱状图(无结论) | ✅ 已完成 |
|
||||
| 13 | 未来关键发展能力分析 | `/report/future-abilities?recordId=3` | 网页截图 | CategoryType=8 雷达图+横向柱状图+结论 | ✅ 已完成 |
|
||||
|
||||
## 静态资源目录
|
||||
|
|
|
|||
|
|
@ -15,21 +15,18 @@
|
|||
else
|
||||
{
|
||||
<div class="ct-page">
|
||||
<div class="ct-section-title">7、性格类型分析</div>
|
||||
<div class="ct-section-title">7、性格类型</div>
|
||||
|
||||
<!-- 上半部分:环形图 + 雷达图 + 柱状图 -->
|
||||
<div class="ct-charts">
|
||||
<!-- 左:环形图 -->
|
||||
<div class="ct-chart-panel ct-panel-donut">
|
||||
<div class="ct-chart-title">性格类型</div>
|
||||
<canvas id="donutChart" width="300" height="300"></canvas>
|
||||
</div>
|
||||
<!-- 中:雷达图 -->
|
||||
<div class="ct-chart-panel">
|
||||
<div class="ct-chart-title">性格类型</div>
|
||||
<canvas id="radarChart" width="340" height="300"></canvas>
|
||||
</div>
|
||||
<!-- 右:竖向柱状图 -->
|
||||
<div class="ct-chart-panel">
|
||||
<div class="ct-chart-title">性格类型排名</div>
|
||||
<canvas id="barChart" width="380" height="300"></canvas>
|
||||
|
|
@ -79,14 +76,12 @@ else
|
|||
|
||||
var labels = [@Html.Raw(string.Join(",", Model.Items.Select(x => $"\"{x.CategoryName}\"")))];
|
||||
var scores = [@Html.Raw(string.Join(",", Model.Items.Select(x => x.Score.ToString("F0"))))];
|
||||
var totalScore = @Model.TotalScore.ToString("F0");
|
||||
var maxScore = Math.ceil(Math.max(...scores) / 2) * 2 + 2;
|
||||
|
||||
var barLabels = [@Html.Raw(string.Join(",", Model.ItemsByScore.Select(x => $"\"{x.CategoryName}\"")))];
|
||||
var barScores = [@Html.Raw(string.Join(",", Model.ItemsByScore.Select(x => x.Score.ToString("F0"))))];
|
||||
var barMax = Math.ceil(Math.max(...barScores) / 2) * 2 + 2;
|
||||
|
||||
// 5种颜色
|
||||
var colors = ['#4A90E2', '#52A06A', '#F5A623', '#E88B9C', '#2ABFBF'];
|
||||
var colorsAlpha = ['rgba(74,144,226,0.7)', 'rgba(82,160,106,0.7)', 'rgba(245,166,35,0.7)', 'rgba(232,139,156,0.7)', 'rgba(42,191,191,0.7)'];
|
||||
|
||||
|
|
@ -117,23 +112,8 @@ else
|
|||
var ctxDonut = document.getElementById('donutChart').getContext('2d');
|
||||
new Chart(ctxDonut, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: scores,
|
||||
backgroundColor: colors,
|
||||
borderWidth: 2,
|
||||
borderColor: '#fff'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: false,
|
||||
cutout: '50%',
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
},
|
||||
layout: { padding: 40 }
|
||||
},
|
||||
data: { labels: labels, datasets: [{ data: scores, backgroundColor: colors, borderWidth: 2, borderColor: '#fff' }] },
|
||||
options: { responsive: false, cutout: '50%', plugins: { legend: { display: false } }, layout: { padding: 40 } },
|
||||
plugins: [donutLabelPlugin]
|
||||
});
|
||||
|
||||
|
|
@ -142,93 +122,19 @@ else
|
|||
var ctxRadar = document.getElementById('radarChart').getContext('2d');
|
||||
new Chart(ctxRadar, {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: scores,
|
||||
backgroundColor: 'rgba(232,139,156,0.12)',
|
||||
borderColor: 'rgba(232,139,156,0.8)',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: 'rgba(232,139,156,0.9)',
|
||||
pointBorderColor: '#fff',
|
||||
pointRadius: 4,
|
||||
pointBorderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
r: {
|
||||
beginAtZero: true,
|
||||
max: maxScore,
|
||||
ticks: { stepSize: 2, font: { size: 10 }, backdropColor: 'transparent', color: '#999' },
|
||||
pointLabels: { font: { size: 12, weight: '500' }, color: '#333', padding: 8 },
|
||||
grid: { color: 'rgba(0,0,0,0.06)' },
|
||||
angleLines: { color: 'rgba(0,0,0,0.06)' }
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [{
|
||||
afterDatasetsDraw: function(chart) {
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
var ctx2 = chart.ctx;
|
||||
ctx2.font = 'bold 11px sans-serif';
|
||||
ctx2.fillStyle = radarColor;
|
||||
ctx2.textBaseline = 'middle';
|
||||
meta.data.forEach(function(point, i) {
|
||||
var val = scores[i];
|
||||
var cx = chart.scales.r.xCenter;
|
||||
var cy = chart.scales.r.yCenter;
|
||||
var dx = point.x - cx, dy = point.y - cy;
|
||||
var dist = Math.sqrt(dx*dx+dy*dy) || 1;
|
||||
ctx2.textAlign = 'center';
|
||||
ctx2.fillText(val, point.x + (dx/dist)*14, point.y + (dy/dist)*14);
|
||||
});
|
||||
}
|
||||
}]
|
||||
data: { labels: labels, datasets: [{ data: scores, backgroundColor: 'rgba(232,139,156,0.12)', borderColor: 'rgba(232,139,156,0.8)', borderWidth: 2, pointBackgroundColor: 'rgba(232,139,156,0.9)', pointBorderColor: '#fff', pointRadius: 4, pointBorderWidth: 1 }] },
|
||||
options: { responsive: false, plugins: { legend: { display: false } }, scales: { r: { beginAtZero: true, max: maxScore, ticks: { stepSize: 2, font: { size: 10 }, backdropColor: 'transparent', color: '#999' }, pointLabels: { font: { size: 12, weight: '500' }, color: '#333', padding: 8 }, grid: { color: 'rgba(0,0,0,0.06)' }, angleLines: { color: 'rgba(0,0,0,0.06)' } } } },
|
||||
plugins: [{ afterDatasetsDraw: function(chart) { var meta = chart.getDatasetMeta(0); var ctx2 = chart.ctx; ctx2.font = 'bold 11px sans-serif'; ctx2.fillStyle = radarColor; ctx2.textBaseline = 'middle'; meta.data.forEach(function(point, i) { var val = scores[i]; var cx = chart.scales.r.xCenter; var cy = chart.scales.r.yCenter; var dx = point.x - cx, dy = point.y - cy; var dist = Math.sqrt(dx*dx+dy*dy) || 1; ctx2.textAlign = 'center'; ctx2.fillText(val, point.x + (dx/dist)*14, point.y + (dy/dist)*14); }); } }]
|
||||
});
|
||||
|
||||
// ---- 竖向柱状图 ----
|
||||
var barDataLabelPlugin = {
|
||||
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.textAlign = 'center';
|
||||
ctx.textBaseline = 'bottom';
|
||||
meta.data.forEach(function(bar, i) {
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.fillText(dataset.data[i], bar.x, bar.y - 4);
|
||||
});
|
||||
}
|
||||
};
|
||||
var barDataLabelPlugin = { 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.textAlign = 'center'; ctx.textBaseline = 'bottom'; meta.data.forEach(function(bar, i) { ctx.fillStyle = '#333'; ctx.fillText(dataset.data[i], bar.x, bar.y - 4); }); } };
|
||||
|
||||
var ctxBar = document.getElementById('barChart').getContext('2d');
|
||||
new Chart(ctxBar, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: barLabels,
|
||||
datasets: [{
|
||||
data: barScores,
|
||||
backgroundColor: colorsAlpha,
|
||||
borderColor: colors,
|
||||
borderWidth: 0,
|
||||
borderRadius: 3,
|
||||
barThickness: 40
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
y: { beginAtZero: true, max: barMax, ticks: { stepSize: 2, font: { size: 10 }, color: '#999' }, grid: { color: 'rgba(0,0,0,0.05)' } },
|
||||
x: { ticks: { font: { size: 11 }, color: '#666' }, grid: { display: false } }
|
||||
},
|
||||
layout: { padding: { top: 20 } }
|
||||
},
|
||||
data: { labels: barLabels, datasets: [{ data: barScores, backgroundColor: colorsAlpha, borderColor: colors, borderWidth: 0, borderRadius: 3, barThickness: 40 }] },
|
||||
options: { responsive: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, max: barMax, ticks: { stepSize: 2, font: { size: 10 }, color: '#999' }, grid: { color: 'rgba(0,0,0,0.05)' } }, x: { ticks: { font: { size: 11 }, color: '#666' }, grid: { display: false } } }, layout: { padding: { top: 20 } } },
|
||||
plugins: [barDataLabelPlugin]
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using MiAssessment.Model.Models.Report;
|
|||
namespace MiAssessment.Api.Pages.Report;
|
||||
|
||||
/// <summary>
|
||||
/// 性格类型分析页 PageModel
|
||||
/// 性格类型分析页 PageModel(三图+结论卡片版)
|
||||
/// 路由:/report/character-types?recordId=3
|
||||
/// CategoryType=7:孔雀(表达型)、老虎(支配型)、猫头鹰(精准型)、变色龙(整合型)、考拉(耐心型)
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
@page "/report/character-types-chart"
|
||||
@model MiAssessment.Api.Pages.Report.CharacterTypesChartModel
|
||||
@{
|
||||
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="ctc-page">
|
||||
<div class="ctc-section-title">7、性格类型</div>
|
||||
|
||||
<div class="ctc-charts">
|
||||
<!-- 左:雷达图 -->
|
||||
<div class="ctc-chart-panel">
|
||||
<div class="ctc-chart-title">性格类型</div>
|
||||
<canvas id="radarChart" width="500" height="620"></canvas>
|
||||
</div>
|
||||
<!-- 右:横向柱状图 -->
|
||||
<div class="ctc-chart-panel">
|
||||
<div class="ctc-chart-title">性格类型排名</div>
|
||||
<canvas id="barChart" width="560" height="620"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@section Styles {
|
||||
<link rel="stylesheet" href="/css/pages/character-types-chart.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;
|
||||
|
||||
var labels = [@Html.Raw(string.Join(",", Model.Items.Select(x => $"\"{x.CategoryName}\"")))];
|
||||
var scores = [@Html.Raw(string.Join(",", Model.Items.Select(x => x.Score.ToString("F0"))))];
|
||||
var maxScore = Math.ceil(Math.max(...scores) / 2) * 2 + 2;
|
||||
|
||||
var barLabels = [@Html.Raw(string.Join(",", Model.ItemsByScore.Select(x => $"\"{x.CategoryName}\"")))];
|
||||
var barScores = [@Html.Raw(string.Join(",", Model.ItemsByScore.Select(x => x.Score.ToString("F0"))))];
|
||||
var barMax = Math.ceil(Math.max(...barScores) / 2) * 2 + 2;
|
||||
|
||||
// 5种颜色(横向柱状图,按排名顺序)
|
||||
var colors = ['#2ABFBF', '#E88B9C', '#F5C842', '#52A06A', '#4A90E2'];
|
||||
|
||||
// ---- 雷达图(蓝色) ----
|
||||
var radarColor = '#4A90E2';
|
||||
var ctxRadar = document.getElementById('radarChart').getContext('2d');
|
||||
new Chart(ctxRadar, {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: scores,
|
||||
backgroundColor: 'rgba(74,144,226,0.15)',
|
||||
borderColor: 'rgba(74,144,226,0.8)',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: 'rgba(74,144,226,0.9)',
|
||||
pointBorderColor: '#fff',
|
||||
pointRadius: 5,
|
||||
pointBorderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
r: {
|
||||
beginAtZero: true,
|
||||
max: maxScore,
|
||||
ticks: { stepSize: 2, font: { size: 13 }, backdropColor: 'transparent', color: '#999' },
|
||||
pointLabels: { font: { size: 15, weight: '500' }, color: '#333', padding: 14 },
|
||||
grid: { color: 'rgba(0,0,0,0.06)' },
|
||||
angleLines: { color: 'rgba(0,0,0,0.06)' }
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [{
|
||||
afterDatasetsDraw: function(chart) {
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
var ctx2 = chart.ctx;
|
||||
ctx2.font = 'bold 14px sans-serif';
|
||||
ctx2.fillStyle = radarColor;
|
||||
ctx2.textBaseline = 'middle';
|
||||
meta.data.forEach(function(point, i) {
|
||||
var val = scores[i];
|
||||
var cx = chart.scales.r.xCenter;
|
||||
var cy = chart.scales.r.yCenter;
|
||||
var dx = point.x - cx, dy = point.y - cy;
|
||||
var dist = Math.sqrt(dx*dx+dy*dy) || 1;
|
||||
ctx2.textAlign = 'center';
|
||||
ctx2.fillText(val, point.x + (dx/dist)*16, point.y + (dy/dist)*16);
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// ---- 横向柱状图 ----
|
||||
var barDataLabelPlugin = {
|
||||
id: 'barDataLabels',
|
||||
afterDatasetsDraw: function(chart) {
|
||||
var ctx = chart.ctx;
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
var dataset = chart.data.datasets[0];
|
||||
ctx.font = 'bold 14px "Microsoft YaHei", sans-serif';
|
||||
ctx.textBaseline = 'middle';
|
||||
meta.data.forEach(function(bar, i) {
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillText(dataset.data[i], bar.x + 8, bar.y);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var ctxBar = document.getElementById('barChart').getContext('2d');
|
||||
new Chart(ctxBar, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: barLabels,
|
||||
datasets: [{
|
||||
data: barScores,
|
||||
backgroundColor: colors,
|
||||
borderWidth: 0,
|
||||
borderRadius: 4,
|
||||
barThickness: 40
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: false,
|
||||
indexAxis: 'y',
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
x: { beginAtZero: true, max: barMax, ticks: { stepSize: 2, font: { size: 13 }, color: '#999' }, grid: { color: 'rgba(0,0,0,0.05)' } },
|
||||
y: { ticks: { font: { size: 14 }, color: '#666' }, grid: { display: false } }
|
||||
},
|
||||
layout: { padding: { right: 30, top: 10 } }
|
||||
},
|
||||
plugins: [barDataLabelPlugin]
|
||||
});
|
||||
|
||||
document.body.setAttribute('data-render-complete', 'true');
|
||||
</script>
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using MiAssessment.Core.Interfaces;
|
||||
using MiAssessment.Model.Data;
|
||||
using MiAssessment.Model.Models.Report;
|
||||
|
||||
namespace MiAssessment.Api.Pages.Report;
|
||||
|
||||
/// <summary>
|
||||
/// 性格类型分析-图表页 PageModel(雷达图+横向柱状图,无结论卡片)
|
||||
/// 路由:/report/character-types-chart?recordId=3
|
||||
/// CategoryType=7
|
||||
/// </summary>
|
||||
public class CharacterTypesChartModel : ReportPageModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 按 CategoryId 排序(雷达图用)
|
||||
/// </summary>
|
||||
public List<CategoryResultDataDto> Items { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 按分数降序(柱状图用)
|
||||
/// </summary>
|
||||
public List<CategoryResultDataDto> ItemsByScore { get; set; } = new();
|
||||
|
||||
public CharacterTypesChartModel(IReportDataService reportDataService)
|
||||
: base(reportDataService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task OnDataLoadedAsync()
|
||||
{
|
||||
if (ReportData?.ResultsByType == null ||
|
||||
!ReportData.ResultsByType.TryGetValue(7, out var allItems))
|
||||
{
|
||||
ErrorMessage = "缺少性格类型数据";
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Items = allItems.OrderBy(x => x.CategoryId).ToList();
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
ErrorMessage = "未找到性格类型数据";
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
ItemsByScore = Items.OrderByDescending(x => x.Score).ToList();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* ============================================
|
||||
性格类型-图表页(雷达图+横向柱状图)
|
||||
页面固定尺寸:1309×926px
|
||||
============================================ */
|
||||
|
||||
.ctc-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.ctc-section-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #E67E73;
|
||||
}
|
||||
|
||||
.ctc-charts {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex: 1;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.ctc-chart-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ctc-chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #E67E73;
|
||||
margin-bottom: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
/* ============================================
|
||||
性格类型分析页
|
||||
性格类型分析页(三图+结论卡片版)
|
||||
页面固定尺寸:1309×926px
|
||||
三图(环形+雷达+柱状)+ 两结论卡片
|
||||
============================================ */
|
||||
|
||||
.ct-page {
|
||||
|
|
@ -18,7 +17,6 @@
|
|||
color: #E67E73;
|
||||
}
|
||||
|
||||
/* ---- 上半部分:三图并排 ---- */
|
||||
.ct-charts {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
|
@ -32,9 +30,7 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.ct-panel-donut {
|
||||
flex: 0.9;
|
||||
}
|
||||
.ct-panel-donut { flex: 0.9; }
|
||||
|
||||
.ct-chart-title {
|
||||
font-size: 16px;
|
||||
|
|
@ -44,7 +40,6 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- 下半部分:结论卡片 ---- */
|
||||
.ct-conclusions {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
|
|
@ -68,8 +63,8 @@
|
|||
}
|
||||
|
||||
.ct-card-weak {
|
||||
border-color: #999999;
|
||||
background: #F8F8F8;
|
||||
border-color: #E67E73;
|
||||
background: #FFF5F5;
|
||||
}
|
||||
|
||||
.ct-badge {
|
||||
|
|
@ -87,7 +82,7 @@
|
|||
}
|
||||
|
||||
.ct-badge-strong { background: #C0392B; }
|
||||
.ct-badge-weak { background: #999999; }
|
||||
.ct-badge-weak { background: #C0392B; }
|
||||
|
||||
.ct-conclusion-content {
|
||||
font-size: 13px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user