feat(admin-dashboard): add mini share chart for plan order top5
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user