213
This commit is contained in:
parent
b24b09557d
commit
086e2c47bb
|
|
@ -17,9 +17,10 @@
|
||||||
| 8.3 | 细分能力精准分析-竖向柱状图(×8) | `/report/sub-ability-detail-v?recordId=3&parentId=8` | 网页截图 | 单智能5项子能力雷达图+竖向柱状图+结论 | ✅ 已完成 |
|
| 8.3 | 细分能力精准分析-竖向柱状图(×8) | `/report/sub-ability-detail-v?recordId=3&parentId=8` | 网页截图 | 单智能5项子能力雷达图+竖向柱状图+结论 | ✅ 已完成 |
|
||||||
| 9 | 先天学习类型分析 | `/report/learning-types?recordId=3` | 网页截图 | CategoryType=4 雷达图+竖向柱状图(多色)+结论 | ✅ 已完成 |
|
| 9 | 先天学习类型分析 | `/report/learning-types?recordId=3` | 网页截图 | CategoryType=4 雷达图+竖向柱状图(多色)+结论 | ✅ 已完成 |
|
||||||
| 10 | 学习关键能力分析 | `/report/learning-abilities?recordId=3` | 网页截图 | CategoryType=5 雷达图+竖向柱状图(多色) | ✅ 已完成 |
|
| 10 | 学习关键能力分析 | `/report/learning-abilities?recordId=3` | 网页截图 | CategoryType=5 雷达图+竖向柱状图(多色) | ✅ 已完成 |
|
||||||
| 11 | 科学大脑类型分析 | `/report/brain-types` | 网页截图 | CategoryType=6 结果+结论 | ⬜ 骨架 |
|
| 10.1 | 学习关键能力结论 | `/report/learning-abilities-conclusion?recordId=3` | 网页截图 | CategoryType=5 最弱能力结论详情(全页文本卡片) | ✅ 已完成 |
|
||||||
| 12 | 性格类型分析 | `/report/character-types` | 网页截图 | CategoryType=7 结果+结论 | ⬜ 骨架 |
|
| 11 | 科学大脑类型分析 | `/report/brain-types?recordId=3` | 网页截图 | CategoryType=6 环形图+雷达图+竖向柱状图+结论 | ✅ 已完成 |
|
||||||
| 13 | 未来关键发展能力分析 | `/report/future-abilities` | 网页截图 | CategoryType=8 结果+结论 | ⬜ 骨架 |
|
| 12 | 性格类型分析 | `/report/character-types?recordId=3` | 网页截图 | CategoryType=7 环形图+雷达图+竖向柱状图+结论 | ✅ 已完成 |
|
||||||
|
| 13 | 未来关键发展能力分析 | `/report/future-abilities?recordId=3` | 网页截图 | CategoryType=8 雷达图+横向柱状图+结论 | ✅ 已完成 |
|
||||||
|
|
||||||
## 静态资源目录
|
## 静态资源目录
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
@page "/report/brain-types"
|
@page "/report/brain-types"
|
||||||
@model MiAssessment.Api.Pages.Report.BrainTypesModel
|
@model MiAssessment.Api.Pages.Report.BrainTypesModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "科学大脑类型分析";
|
ViewData["Title"] = "科学大脑类型";
|
||||||
ViewData["PageTitle"] = "科学大脑类型分析";
|
ViewData["PageTitle"] = null;
|
||||||
|
ViewData["PageNumber"] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!Model.IsSuccess)
|
@if (!Model.IsSuccess)
|
||||||
|
|
@ -13,7 +14,225 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="brain-types-content">
|
<div class="bt-page">
|
||||||
<p class="no-content">科学大脑类型分析占位 - 具体内容后续实现</p>
|
<div class="bt-section-title">6、科学大脑类型</div>
|
||||||
|
|
||||||
|
<!-- 上半部分:环形图 + 雷达图 + 柱状图 -->
|
||||||
|
<div class="bt-charts">
|
||||||
|
<!-- 左:环形图 -->
|
||||||
|
<div class="bt-chart-panel bt-panel-donut">
|
||||||
|
<div class="bt-chart-title">科学大脑类型</div>
|
||||||
|
<canvas id="donutChart" width="300" height="300"></canvas>
|
||||||
|
</div>
|
||||||
|
<!-- 中:雷达图 -->
|
||||||
|
<div class="bt-chart-panel">
|
||||||
|
<div class="bt-chart-title">科学大脑类型</div>
|
||||||
|
<canvas id="radarChart" width="340" height="300"></canvas>
|
||||||
|
</div>
|
||||||
|
<!-- 右:竖向柱状图 -->
|
||||||
|
<div class="bt-chart-panel">
|
||||||
|
<div class="bt-chart-title">科学大脑排名</div>
|
||||||
|
<canvas id="barChart" width="380" height="300"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下半部分:结论卡片 -->
|
||||||
|
<div class="bt-conclusions">
|
||||||
|
<div class="bt-conclusion-card bt-card-strong">
|
||||||
|
<div class="bt-badge bt-badge-strong">最强能力解读</div>
|
||||||
|
<div class="bt-conclusion-content">
|
||||||
|
@if (Model.StrongestConclusion != null)
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.StrongestConclusion.Content?.Replace("\\n", "<br/>").Replace("\n", "<br/>") ?? "暂无结论")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="no-content">暂无结论数据</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bt-conclusion-card bt-card-weak">
|
||||||
|
<div class="bt-badge bt-badge-weak">较弱能力解读</div>
|
||||||
|
<div class="bt-conclusion-content">
|
||||||
|
@if (Model.WeakestConclusion != null)
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.WeakestConclusion.Content?.Replace("\\n", "<br/>").Replace("\n", "<br/>") ?? "暂无结论")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="no-content">暂无结论数据</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<link rel="stylesheet" href="/css/pages/brain-types.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 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)'];
|
||||||
|
|
||||||
|
// ---- 环形图 ----
|
||||||
|
var donutLabelPlugin = {
|
||||||
|
id: 'donutLabels',
|
||||||
|
afterDatasetsDraw: function(chart) {
|
||||||
|
var ctx = chart.ctx;
|
||||||
|
var meta = chart.getDatasetMeta(0);
|
||||||
|
var dataset = chart.data.datasets[0];
|
||||||
|
var total = dataset.data.reduce(function(a, b) { return a + b; }, 0);
|
||||||
|
ctx.font = 'bold 12px "Microsoft YaHei", sans-serif';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
meta.data.forEach(function(arc, i) {
|
||||||
|
var pct = (dataset.data[i] / total * 100).toFixed(2) + '%';
|
||||||
|
var name = chart.data.labels[i];
|
||||||
|
var angle = (arc.startAngle + arc.endAngle) / 2;
|
||||||
|
var r = (arc.outerRadius + arc.innerRadius) / 2;
|
||||||
|
var x = arc.x + Math.cos(angle) * (arc.outerRadius + 30);
|
||||||
|
var y = arc.y + Math.sin(angle) * (arc.outerRadius + 30);
|
||||||
|
ctx.fillStyle = dataset.backgroundColor[i];
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(name, x, y - 8);
|
||||||
|
ctx.fillText(pct, x, y + 8);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 }
|
||||||
|
},
|
||||||
|
plugins: [donutLabelPlugin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- 雷达图 ----
|
||||||
|
var radarColor = '#E88B9C';
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- 竖向柱状图 ----
|
||||||
|
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 } }
|
||||||
|
},
|
||||||
|
plugins: [barDataLabelPlugin]
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.setAttribute('data-render-complete', 'true');
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,111 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MiAssessment.Core.Interfaces;
|
using MiAssessment.Core.Interfaces;
|
||||||
|
using MiAssessment.Model.Data;
|
||||||
|
using MiAssessment.Model.Models.Report;
|
||||||
|
|
||||||
namespace MiAssessment.Api.Pages.Report;
|
namespace MiAssessment.Api.Pages.Report;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 科学大脑类型分析 PageModel
|
/// 科学大脑类型分析页 PageModel
|
||||||
|
/// 路由:/report/brain-types?recordId=3
|
||||||
|
/// CategoryType=6:智力脑、创意脑、语言脑、运动脑、情绪脑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BrainTypesModel : ReportPageModelBase
|
public class BrainTypesModel : ReportPageModelBase
|
||||||
{
|
{
|
||||||
public BrainTypesModel(IReportDataService reportDataService)
|
/// <summary>
|
||||||
|
/// 按 CategoryId 排序(雷达图/环形图用)
|
||||||
|
/// </summary>
|
||||||
|
public List<CategoryResultDataDto> Items { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按分数降序(柱状图用)
|
||||||
|
/// </summary>
|
||||||
|
public List<CategoryResultDataDto> ItemsByScore { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总分(环形图百分比计算用)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalScore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强类型名称
|
||||||
|
/// </summary>
|
||||||
|
public string StrongestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强类型结论
|
||||||
|
/// </summary>
|
||||||
|
public ConclusionDataDto? StrongestConclusion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 较弱类型名称
|
||||||
|
/// </summary>
|
||||||
|
public string WeakestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 较弱类型结论
|
||||||
|
/// </summary>
|
||||||
|
public ConclusionDataDto? WeakestConclusion { get; set; }
|
||||||
|
|
||||||
|
private readonly MiAssessmentDbContext _dbContext;
|
||||||
|
|
||||||
|
public BrainTypesModel(IReportDataService reportDataService, MiAssessmentDbContext dbContext)
|
||||||
: base(reportDataService)
|
: base(reportDataService)
|
||||||
{
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnDataLoadedAsync()
|
||||||
|
{
|
||||||
|
if (ReportData?.ResultsByType == null ||
|
||||||
|
!ReportData.ResultsByType.TryGetValue(6, out var allItems))
|
||||||
|
{
|
||||||
|
ErrorMessage = "缺少科学大脑类型数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items = allItems.OrderBy(x => x.CategoryId).ToList();
|
||||||
|
if (Items.Count == 0)
|
||||||
|
{
|
||||||
|
ErrorMessage = "未找到科学大脑类型数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemsByScore = Items.OrderByDescending(x => x.Score).ToList();
|
||||||
|
TotalScore = Items.Sum(x => x.Score);
|
||||||
|
|
||||||
|
// 最强结论
|
||||||
|
var strongest = ItemsByScore.First();
|
||||||
|
StrongestName = strongest.CategoryName;
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(strongest.CategoryId, out var sc))
|
||||||
|
StrongestConclusion = sc;
|
||||||
|
else
|
||||||
|
StrongestConclusion = await GetTemplateConclusionAsync(strongest.CategoryId, 1);
|
||||||
|
|
||||||
|
// 较弱结论
|
||||||
|
var weakest = ItemsByScore.Last();
|
||||||
|
WeakestName = weakest.CategoryName;
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(weakest.CategoryId, out var wc))
|
||||||
|
WeakestConclusion = wc;
|
||||||
|
else
|
||||||
|
WeakestConclusion = await GetTemplateConclusionAsync(weakest.CategoryId, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ConclusionDataDto?> GetTemplateConclusionAsync(long categoryId, int conclusionType)
|
||||||
|
{
|
||||||
|
var template = await _dbContext.ReportConclusions
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t =>
|
||||||
|
t.CategoryId == categoryId &&
|
||||||
|
t.ConclusionType == conclusionType &&
|
||||||
|
!t.IsDeleted);
|
||||||
|
if (template == null) return null;
|
||||||
|
return new ConclusionDataDto
|
||||||
|
{
|
||||||
|
CategoryId = template.CategoryId,
|
||||||
|
ConclusionType = template.ConclusionType,
|
||||||
|
Title = template.Title,
|
||||||
|
Content = template.Content
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
@model MiAssessment.Api.Pages.Report.CharacterTypesModel
|
@model MiAssessment.Api.Pages.Report.CharacterTypesModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "性格类型分析";
|
ViewData["Title"] = "性格类型分析";
|
||||||
ViewData["PageTitle"] = "性格类型分析";
|
ViewData["PageTitle"] = null;
|
||||||
|
ViewData["PageNumber"] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!Model.IsSuccess)
|
@if (!Model.IsSuccess)
|
||||||
|
|
@ -13,7 +14,224 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="character-types-content">
|
<div class="ct-page">
|
||||||
<p class="no-content">性格类型分析占位 - 具体内容后续实现</p>
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下半部分:结论卡片 -->
|
||||||
|
<div class="ct-conclusions">
|
||||||
|
<div class="ct-conclusion-card ct-card-strong">
|
||||||
|
<div class="ct-badge ct-badge-strong">最强能力解读</div>
|
||||||
|
<div class="ct-conclusion-content">
|
||||||
|
@if (Model.StrongestConclusion != null)
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.StrongestConclusion.Content?.Replace("\\n", "<br/>").Replace("\n", "<br/>") ?? "暂无结论")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="no-content">暂无结论数据</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ct-conclusion-card ct-card-weak">
|
||||||
|
<div class="ct-badge ct-badge-weak">较弱能力解读</div>
|
||||||
|
<div class="ct-conclusion-content">
|
||||||
|
@if (Model.WeakestConclusion != null)
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.WeakestConclusion.Content?.Replace("\\n", "<br/>").Replace("\n", "<br/>") ?? "暂无结论")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="no-content">暂无结论数据</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<link rel="stylesheet" href="/css/pages/character-types.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 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)'];
|
||||||
|
|
||||||
|
// ---- 环形图 ----
|
||||||
|
var donutLabelPlugin = {
|
||||||
|
id: 'donutLabels',
|
||||||
|
afterDatasetsDraw: function(chart) {
|
||||||
|
var ctx = chart.ctx;
|
||||||
|
var meta = chart.getDatasetMeta(0);
|
||||||
|
var dataset = chart.data.datasets[0];
|
||||||
|
var total = dataset.data.reduce(function(a, b) { return a + b; }, 0);
|
||||||
|
ctx.font = 'bold 12px "Microsoft YaHei", sans-serif';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
meta.data.forEach(function(arc, i) {
|
||||||
|
var pct = (dataset.data[i] / total * 100).toFixed(2) + '%';
|
||||||
|
var name = chart.data.labels[i];
|
||||||
|
var angle = (arc.startAngle + arc.endAngle) / 2;
|
||||||
|
var x = arc.x + Math.cos(angle) * (arc.outerRadius + 30);
|
||||||
|
var y = arc.y + Math.sin(angle) * (arc.outerRadius + 30);
|
||||||
|
ctx.fillStyle = dataset.backgroundColor[i];
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(name, x, y - 8);
|
||||||
|
ctx.fillText(pct, x, y + 8);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 }
|
||||||
|
},
|
||||||
|
plugins: [donutLabelPlugin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- 雷达图 ----
|
||||||
|
var radarColor = '#E88B9C';
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- 竖向柱状图 ----
|
||||||
|
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 } }
|
||||||
|
},
|
||||||
|
plugins: [barDataLabelPlugin]
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.setAttribute('data-render-complete', 'true');
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,111 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MiAssessment.Core.Interfaces;
|
using MiAssessment.Core.Interfaces;
|
||||||
|
using MiAssessment.Model.Data;
|
||||||
|
using MiAssessment.Model.Models.Report;
|
||||||
|
|
||||||
namespace MiAssessment.Api.Pages.Report;
|
namespace MiAssessment.Api.Pages.Report;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 性格类型分析 PageModel
|
/// 性格类型分析页 PageModel
|
||||||
|
/// 路由:/report/character-types?recordId=3
|
||||||
|
/// CategoryType=7:孔雀(表达型)、老虎(支配型)、猫头鹰(精准型)、变色龙(整合型)、考拉(耐心型)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CharacterTypesModel : ReportPageModelBase
|
public class CharacterTypesModel : ReportPageModelBase
|
||||||
{
|
{
|
||||||
public CharacterTypesModel(IReportDataService reportDataService)
|
/// <summary>
|
||||||
|
/// 按 CategoryId 排序(雷达图/环形图用)
|
||||||
|
/// </summary>
|
||||||
|
public List<CategoryResultDataDto> Items { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按分数降序(柱状图用)
|
||||||
|
/// </summary>
|
||||||
|
public List<CategoryResultDataDto> ItemsByScore { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总分(环形图百分比计算用)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalScore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强类型名称
|
||||||
|
/// </summary>
|
||||||
|
public string StrongestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强类型结论
|
||||||
|
/// </summary>
|
||||||
|
public ConclusionDataDto? StrongestConclusion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 较弱类型名称
|
||||||
|
/// </summary>
|
||||||
|
public string WeakestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 较弱类型结论
|
||||||
|
/// </summary>
|
||||||
|
public ConclusionDataDto? WeakestConclusion { get; set; }
|
||||||
|
|
||||||
|
private readonly MiAssessmentDbContext _dbContext;
|
||||||
|
|
||||||
|
public CharacterTypesModel(IReportDataService reportDataService, MiAssessmentDbContext dbContext)
|
||||||
: base(reportDataService)
|
: base(reportDataService)
|
||||||
{
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnDataLoadedAsync()
|
||||||
|
{
|
||||||
|
if (ReportData?.ResultsByType == null ||
|
||||||
|
!ReportData.ResultsByType.TryGetValue(7, out var allItems))
|
||||||
|
{
|
||||||
|
ErrorMessage = "缺少性格类型数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items = allItems.OrderBy(x => x.CategoryId).ToList();
|
||||||
|
if (Items.Count == 0)
|
||||||
|
{
|
||||||
|
ErrorMessage = "未找到性格类型数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemsByScore = Items.OrderByDescending(x => x.Score).ToList();
|
||||||
|
TotalScore = Items.Sum(x => x.Score);
|
||||||
|
|
||||||
|
// 最强结论
|
||||||
|
var strongest = ItemsByScore.First();
|
||||||
|
StrongestName = strongest.CategoryName;
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(strongest.CategoryId, out var sc))
|
||||||
|
StrongestConclusion = sc;
|
||||||
|
else
|
||||||
|
StrongestConclusion = await GetTemplateConclusionAsync(strongest.CategoryId, 1);
|
||||||
|
|
||||||
|
// 较弱结论
|
||||||
|
var weakest = ItemsByScore.Last();
|
||||||
|
WeakestName = weakest.CategoryName;
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(weakest.CategoryId, out var wc))
|
||||||
|
WeakestConclusion = wc;
|
||||||
|
else
|
||||||
|
WeakestConclusion = await GetTemplateConclusionAsync(weakest.CategoryId, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ConclusionDataDto?> GetTemplateConclusionAsync(long categoryId, int conclusionType)
|
||||||
|
{
|
||||||
|
var template = await _dbContext.ReportConclusions
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t =>
|
||||||
|
t.CategoryId == categoryId &&
|
||||||
|
t.ConclusionType == conclusionType &&
|
||||||
|
!t.IsDeleted);
|
||||||
|
if (template == null) return null;
|
||||||
|
return new ConclusionDataDto
|
||||||
|
{
|
||||||
|
CategoryId = template.CategoryId,
|
||||||
|
ConclusionType = template.ConclusionType,
|
||||||
|
Title = template.Title,
|
||||||
|
Content = template.Content
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
@model MiAssessment.Api.Pages.Report.FutureAbilitiesModel
|
@model MiAssessment.Api.Pages.Report.FutureAbilitiesModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "未来关键发展能力分析";
|
ViewData["Title"] = "未来关键发展能力分析";
|
||||||
ViewData["PageTitle"] = "未来关键发展能力分析";
|
ViewData["PageTitle"] = null;
|
||||||
|
ViewData["PageNumber"] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!Model.IsSuccess)
|
@if (!Model.IsSuccess)
|
||||||
|
|
@ -13,7 +14,177 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="future-abilities-content">
|
<div class="fa-page">
|
||||||
<p class="no-content">未来关键发展能力分析占位 - 具体内容后续实现</p>
|
<div class="fa-section-title">8、未来关键发展能力</div>
|
||||||
|
|
||||||
|
<!-- 上半部分:雷达图 + 横向柱状图 -->
|
||||||
|
<div class="fa-charts">
|
||||||
|
<!-- 左:雷达图 -->
|
||||||
|
<div class="fa-chart-panel">
|
||||||
|
<div class="fa-chart-title">未来关键发展能力</div>
|
||||||
|
<canvas id="radarChart" width="440" height="360"></canvas>
|
||||||
|
</div>
|
||||||
|
<!-- 右:横向柱状图 -->
|
||||||
|
<div class="fa-chart-panel">
|
||||||
|
<div class="fa-chart-title">未来关键发展能力排名</div>
|
||||||
|
<canvas id="barChart" width="500" height="360"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下半部分:结论卡片 -->
|
||||||
|
<div class="fa-conclusions">
|
||||||
|
<div class="fa-conclusion-card fa-card-strong">
|
||||||
|
<div class="fa-badge fa-badge-strong">最强能力解读</div>
|
||||||
|
<div class="fa-conclusion-content">
|
||||||
|
@if (Model.StrongestConclusion != null)
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.StrongestConclusion.Content?.Replace("\\n", "<br/>").Replace("\n", "<br/>") ?? "暂无结论")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="no-content">暂无结论数据</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fa-conclusion-card fa-card-weak">
|
||||||
|
<div class="fa-badge fa-badge-weak">较弱能力解读</div>
|
||||||
|
<div class="fa-conclusion-content">
|
||||||
|
@if (Model.WeakestConclusion != null)
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.WeakestConclusion.Content?.Replace("\\n", "<br/>").Replace("\n", "<br/>") ?? "暂无结论")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="no-content">暂无结论数据</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<link rel="stylesheet" href="/css/pages/future-abilities.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;
|
||||||
|
|
||||||
|
// 10种颜色
|
||||||
|
var colors = ['#4A90E2', '#52A06A', '#F5A623', '#E88B9C', '#2ABFBF', '#9B59B6', '#E67E73', '#3498DB', '#1ABC9C', '#F39C12'];
|
||||||
|
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)', 'rgba(155,89,182,0.7)',
|
||||||
|
'rgba(230,126,115,0.7)', 'rgba(52,152,219,0.7)', 'rgba(26,188,156,0.7)',
|
||||||
|
'rgba(243,156,18,0.7)'
|
||||||
|
];
|
||||||
|
|
||||||
|
// ---- 雷达图 ----
|
||||||
|
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.12)',
|
||||||
|
borderColor: 'rgba(74,144,226,0.8)',
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: 'rgba(74,144,226,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: 9 }, backdropColor: 'transparent', color: '#999' },
|
||||||
|
pointLabels: { font: { size: 11, weight: '500' }, color: '#333', padding: 10 },
|
||||||
|
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 10px 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 11px "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 + 6, bar.y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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: 22
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: false,
|
||||||
|
indexAxis: 'y',
|
||||||
|
plugins: { legend: { display: false } },
|
||||||
|
scales: {
|
||||||
|
x: { beginAtZero: true, max: barMax, ticks: { stepSize: 2, font: { size: 10 }, color: '#999' }, grid: { color: 'rgba(0,0,0,0.05)' } },
|
||||||
|
y: { ticks: { font: { size: 11 }, color: '#666' }, grid: { display: false } }
|
||||||
|
},
|
||||||
|
layout: { padding: { right: 30 } }
|
||||||
|
},
|
||||||
|
plugins: [barDataLabelPlugin]
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.setAttribute('data-render-complete', 'true');
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,111 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MiAssessment.Core.Interfaces;
|
using MiAssessment.Core.Interfaces;
|
||||||
|
using MiAssessment.Model.Data;
|
||||||
|
using MiAssessment.Model.Models.Report;
|
||||||
|
|
||||||
namespace MiAssessment.Api.Pages.Report;
|
namespace MiAssessment.Api.Pages.Report;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 未来关键发展能力分析 PageModel
|
/// 未来关键发展能力分析页 PageModel
|
||||||
|
/// 路由:/report/future-abilities?recordId=3
|
||||||
|
/// CategoryType=8:10项未来能力
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FutureAbilitiesModel : ReportPageModelBase
|
public class FutureAbilitiesModel : ReportPageModelBase
|
||||||
{
|
{
|
||||||
public FutureAbilitiesModel(IReportDataService reportDataService)
|
/// <summary>
|
||||||
|
/// 按 CategoryId 排序(雷达图用)
|
||||||
|
/// </summary>
|
||||||
|
public List<CategoryResultDataDto> Items { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按分数降序(柱状图用)
|
||||||
|
/// </summary>
|
||||||
|
public List<CategoryResultDataDto> ItemsByScore { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总分(环形图百分比计算用)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalScore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强能力名称
|
||||||
|
/// </summary>
|
||||||
|
public string StrongestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强能力结论
|
||||||
|
/// </summary>
|
||||||
|
public ConclusionDataDto? StrongestConclusion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 较弱能力名称
|
||||||
|
/// </summary>
|
||||||
|
public string WeakestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 较弱能力结论
|
||||||
|
/// </summary>
|
||||||
|
public ConclusionDataDto? WeakestConclusion { get; set; }
|
||||||
|
|
||||||
|
private readonly MiAssessmentDbContext _dbContext;
|
||||||
|
|
||||||
|
public FutureAbilitiesModel(IReportDataService reportDataService, MiAssessmentDbContext dbContext)
|
||||||
: base(reportDataService)
|
: base(reportDataService)
|
||||||
{
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnDataLoadedAsync()
|
||||||
|
{
|
||||||
|
if (ReportData?.ResultsByType == null ||
|
||||||
|
!ReportData.ResultsByType.TryGetValue(8, out var allItems))
|
||||||
|
{
|
||||||
|
ErrorMessage = "缺少未来关键发展能力数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items = allItems.OrderBy(x => x.CategoryId).ToList();
|
||||||
|
if (Items.Count == 0)
|
||||||
|
{
|
||||||
|
ErrorMessage = "未找到未来关键发展能力数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemsByScore = Items.OrderByDescending(x => x.Score).ToList();
|
||||||
|
TotalScore = Items.Sum(x => x.Score);
|
||||||
|
|
||||||
|
// 最强结论
|
||||||
|
var strongest = ItemsByScore.First();
|
||||||
|
StrongestName = strongest.CategoryName;
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(strongest.CategoryId, out var sc))
|
||||||
|
StrongestConclusion = sc;
|
||||||
|
else
|
||||||
|
StrongestConclusion = await GetTemplateConclusionAsync(strongest.CategoryId, 1);
|
||||||
|
|
||||||
|
// 较弱结论
|
||||||
|
var weakest = ItemsByScore.Last();
|
||||||
|
WeakestName = weakest.CategoryName;
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(weakest.CategoryId, out var wc))
|
||||||
|
WeakestConclusion = wc;
|
||||||
|
else
|
||||||
|
WeakestConclusion = await GetTemplateConclusionAsync(weakest.CategoryId, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ConclusionDataDto?> GetTemplateConclusionAsync(long categoryId, int conclusionType)
|
||||||
|
{
|
||||||
|
var template = await _dbContext.ReportConclusions
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t =>
|
||||||
|
t.CategoryId == categoryId &&
|
||||||
|
t.ConclusionType == conclusionType &&
|
||||||
|
!t.IsDeleted);
|
||||||
|
if (template == null) return null;
|
||||||
|
return new ConclusionDataDto
|
||||||
|
{
|
||||||
|
CategoryId = template.CategoryId,
|
||||||
|
ConclusionType = template.ConclusionType,
|
||||||
|
Title = template.Title,
|
||||||
|
Content = template.Content
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
@page "/report/learning-abilities-conclusion"
|
||||||
|
@model MiAssessment.Api.Pages.Report.LearningAbilitiesConclusionModel
|
||||||
|
@{
|
||||||
|
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="lac-page">
|
||||||
|
<!-- 全页结论卡片 -->
|
||||||
|
<div class="lac-card">
|
||||||
|
<!-- 顶部红色 badge -->
|
||||||
|
<div class="lac-badge">学习关键能力</div>
|
||||||
|
|
||||||
|
<!-- 结论文字内容 -->
|
||||||
|
<div class="lac-content">
|
||||||
|
@Html.Raw(Model.ConclusionContent.Replace("\\n", "<br/>").Replace("\n", "<br/>"))
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<link rel="stylesheet" href="/css/pages/learning-abilities-conclusion.css" />
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script>
|
||||||
|
document.body.setAttribute('data-render-complete', 'true');
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MiAssessment.Core.Interfaces;
|
||||||
|
using MiAssessment.Model.Data;
|
||||||
|
using MiAssessment.Model.Models.Report;
|
||||||
|
|
||||||
|
namespace MiAssessment.Api.Pages.Report;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学习关键能力结论页 PageModel
|
||||||
|
/// 路由:/report/learning-abilities-conclusion?recordId=3
|
||||||
|
/// 展示最弱学习能力的详细结论
|
||||||
|
/// </summary>
|
||||||
|
public class LearningAbilitiesConclusionModel : ReportPageModelBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 最弱能力名称
|
||||||
|
/// </summary>
|
||||||
|
public string WeakestName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结论内容
|
||||||
|
/// </summary>
|
||||||
|
public string ConclusionContent { get; set; } = "";
|
||||||
|
|
||||||
|
private readonly MiAssessmentDbContext _dbContext;
|
||||||
|
|
||||||
|
public LearningAbilitiesConclusionModel(IReportDataService reportDataService, MiAssessmentDbContext dbContext)
|
||||||
|
: base(reportDataService)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnDataLoadedAsync()
|
||||||
|
{
|
||||||
|
if (ReportData?.ResultsByType == null ||
|
||||||
|
!ReportData.ResultsByType.TryGetValue(5, out var allItems))
|
||||||
|
{
|
||||||
|
ErrorMessage = "缺少学习关键能力数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = allItems.OrderByDescending(x => x.Score).ToList();
|
||||||
|
if (items.Count == 0)
|
||||||
|
{
|
||||||
|
ErrorMessage = "未找到学习关键能力数据";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最弱能力
|
||||||
|
var weakest = items.Last();
|
||||||
|
WeakestName = weakest.CategoryName;
|
||||||
|
|
||||||
|
// 从测评记录结论中查找
|
||||||
|
if (ReportData.ConclusionsByCategory.TryGetValue(weakest.CategoryId, out var wc) && wc.Content != null)
|
||||||
|
{
|
||||||
|
ConclusionContent = wc.Content;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// fallback: 从模板表查 ConclusionType=4
|
||||||
|
var template = await _dbContext.ReportConclusions
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t =>
|
||||||
|
t.CategoryId == weakest.CategoryId &&
|
||||||
|
t.ConclusionType == 4 &&
|
||||||
|
!t.IsDeleted);
|
||||||
|
|
||||||
|
ConclusionContent = template?.Content ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* ============================================
|
||||||
|
科学大脑类型分析页
|
||||||
|
页面固定尺寸:1309×926px
|
||||||
|
三图(环形+雷达+柱状)+ 两结论卡片
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.bt-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-section-title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #E67E73;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 上半部分:三图并排 ---- */
|
||||||
|
.bt-charts {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-chart-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-panel-donut {
|
||||||
|
flex: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-chart-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #E67E73;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 下半部分:结论卡片 ---- */
|
||||||
|
.bt-conclusions {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-conclusion-card {
|
||||||
|
flex: 1;
|
||||||
|
border: 3px solid;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 34px 20px 14px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-card-strong {
|
||||||
|
border-color: #E67E73;
|
||||||
|
background: #FFF5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-card-weak {
|
||||||
|
border-color: #E67E73;
|
||||||
|
background: #FFF5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-1px);
|
||||||
|
padding: 5px 24px;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
white-space: nowrap;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-badge-strong { background: #C0392B; }
|
||||||
|
.bt-badge-weak { background: #C0392B; }
|
||||||
|
|
||||||
|
.bt-conclusion-content {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 600;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 9;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* ============================================
|
||||||
|
性格类型分析页
|
||||||
|
页面固定尺寸:1309×926px
|
||||||
|
三图(环形+雷达+柱状)+ 两结论卡片
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.ct-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-section-title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #E67E73;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 上半部分:三图并排 ---- */
|
||||||
|
.ct-charts {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-chart-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-panel-donut {
|
||||||
|
flex: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-chart-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #E67E73;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 下半部分:结论卡片 ---- */
|
||||||
|
.ct-conclusions {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-conclusion-card {
|
||||||
|
flex: 1;
|
||||||
|
border: 3px solid;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 34px 20px 14px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-card-strong {
|
||||||
|
border-color: #E67E73;
|
||||||
|
background: #FFF5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-card-weak {
|
||||||
|
border-color: #999999;
|
||||||
|
background: #F8F8F8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-1px);
|
||||||
|
padding: 5px 24px;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
white-space: nowrap;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ct-badge-strong { background: #C0392B; }
|
||||||
|
.ct-badge-weak { background: #999999; }
|
||||||
|
|
||||||
|
.ct-conclusion-content {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 600;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 9;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* ============================================
|
||||||
|
未来关键发展能力分析页
|
||||||
|
页面固定尺寸:1309×926px
|
||||||
|
雷达图 + 横向柱状图 + 两结论卡片
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.fa-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-section-title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #E67E73;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 上半部分:两图并排 ---- */
|
||||||
|
.fa-charts {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-chart-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-chart-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #E67E73;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 下半部分:结论卡片 ---- */
|
||||||
|
.fa-conclusions {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-conclusion-card {
|
||||||
|
flex: 1;
|
||||||
|
border: 3px solid;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 34px 20px 14px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-card-strong {
|
||||||
|
border-color: #E67E73;
|
||||||
|
background: #FFF5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-card-weak {
|
||||||
|
border-color: #999999;
|
||||||
|
background: #F8F8F8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-1px);
|
||||||
|
padding: 5px 24px;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
white-space: nowrap;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-badge-strong { background: #C0392B; }
|
||||||
|
.fa-badge-weak { background: #999999; }
|
||||||
|
|
||||||
|
.fa-conclusion-content {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 600;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 9;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* ============================================
|
||||||
|
学习关键能力结论页
|
||||||
|
页面固定尺寸:1309×926px
|
||||||
|
全页红色边框卡片 + 顶部红色badge
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.lac-page {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全页卡片 */
|
||||||
|
.lac-card {
|
||||||
|
flex: 1;
|
||||||
|
border: 3px solid #C0392B;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 44px 36px 28px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部红色 badge */
|
||||||
|
.lac-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-1px);
|
||||||
|
padding: 6px 40px;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
background: #C0392B;
|
||||||
|
white-space: nowrap;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 结论文字 */
|
||||||
|
.lac-content {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 2;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user