feat(admin-dashboard): add mini share chart for plan order top5

This commit is contained in:
萝卜
2026-03-16 10:25:30 +08:00
parent 1149565702
commit 765f79fe87
4 changed files with 137 additions and 0 deletions

View File

@@ -415,6 +415,67 @@
color:var(--adm-text, #0f172a);
}
/* 可复用迷你占比Top5渐进增强由 admin.js 渲染,页面无 JS 时为空但不影响表格) */
.adm-mini-share{
margin-top:10px;
width:100%;
display:flex;
flex-direction:column;
gap:8px;
padding:10px 10px;
border:1px solid var(--adm-border-color, #e5e7eb);
border-radius:var(--adm-radius, 12px);
background:var(--adm-surface-tint, rgba(15, 23, 42, .02));
box-shadow:var(--adm-shadow-sm);
}
.adm-mini-share.is-empty{
align-items:center;
justify-content:center;
color:var(--adm-text-muted, #94a3b8);
font-size:12px;
}
.adm-mini-share-row{
display:flex;
align-items:center;
gap:10px;
}
.adm-mini-share-name{
width:72px;
min-width:72px;
max-width:72px;
font-size:12px;
color:var(--adm-text-secondary, #64748b);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.adm-mini-share-bar-wrap{
flex:1;
min-width:0;
height:12px;
border-radius:999px;
background:rgba(15, 23, 42, .06);
overflow:hidden;
}
.adm-mini-share-bar{
height:100%;
border-radius:999px;
background:linear-gradient(90deg, rgba(245, 158, 11, .45), rgba(245, 158, 11, .16));
}
.adm-mini-share-value{
width:92px;
min-width:92px;
text-align:right;
font-size:12px;
color:var(--adm-text, #0f172a);
}
.kpi-grid{
display:grid;
grid-template-columns:repeat(4,minmax(0,1fr));

View File

@@ -195,6 +195,68 @@
});
})();
// 仪表盘:迷你占比(套餐订单占比 Top5
(function () {
var el = qs('[data-role="plan-order-share-top5-chart"][data-points]');
if (!el) {
return;
}
var raw = el.getAttribute('data-points') || '[]';
var points = [];
try {
points = JSON.parse(raw) || [];
} catch (e) {
points = [];
}
var total = Number(el.getAttribute('data-total') || 0);
if (!total || total <= 0) {
total = 0;
}
if (!points || points.length === 0 || total === 0) {
el.classList.add('is-empty');
el.textContent = '暂无占比数据';
return;
}
el.innerHTML = '';
points.forEach(function (p, idx) {
var cnt = Number(p && p.count ? p.count : 0);
var ratio = total > 0 ? Math.max(0, Math.min(1, cnt / total)) : 0;
var row = document.createElement('div');
row.className = 'adm-mini-share-row';
var name = document.createElement('div');
name.className = 'adm-mini-share-name';
name.textContent = '#' + (idx + 1);
var wrap = document.createElement('div');
wrap.className = 'adm-mini-share-bar-wrap';
var bar = document.createElement('div');
bar.className = 'adm-mini-share-bar';
bar.style.width = Math.round(ratio * 100) + '%';
wrap.appendChild(bar);
var val = document.createElement('div');
val.className = 'adm-mini-share-value';
val.textContent = (ratio * 100).toFixed(1) + '%';
row.title = 'Top' + (idx + 1) + '' + cnt + ' 单,占比 ' + (ratio * 100).toFixed(1) + '%';
row.appendChild(name);
row.appendChild(wrap);
row.appendChild(val);
el.appendChild(row);
});
})();
// 通用:将后端 flash 信息同步到 toast更像 Ant Design Pro 的反馈方式)
// 说明:渐进增强。页面仍保留原本的提示块,不依赖 JS。
(function () {

View File

@@ -342,6 +342,19 @@
}
@endphp
@php
// 用于前端渐进增强渲染占比条形图JS 读取 data-points
$sharePoints = [];
foreach ($shareRows as $r) {
$sharePoints[] = [
'plan_id' => (int) ($r['plan_id'] ?? 0),
'count' => (int) ($r['count'] ?? 0),
];
}
@endphp
<div class="adm-mini-share" data-role="plan-order-share-top5-chart" data-total="{{ (int) $totalOrders }}" data-points='@json($sharePoints)'></div>
<table>
<thead>
<tr>

View File

@@ -29,6 +29,7 @@ class AdminDashboardPlanOrderShareCardShouldRenderTest extends TestCase
$res->assertSee('套餐订单占比', false);
$res->assertSee('Top5', false);
$res->assertSee('查看套餐', false);
$res->assertSee('data-role="plan-order-share-top5-chart"', false);
// 有数据时至少应包含百分号展示
$res->assertSee('%', false);