Files
saasshop/resources/views/admin/dashboard.blade.php

542 lines
30 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('admin.layouts.app')
@section('title', '总台仪表盘')
@section('page_title', '总台仪表盘')
@section('content')
@php
$incomingBack = (string) request()->query('back', '');
$safeBackForLinks = \App\Support\BackUrl::sanitizeForLinks($incomingBack);
// 用于构建“从仪表盘跳到其它治理页后可返回仪表盘”的 back
$selfWithoutBack = \App\Support\BackUrl::selfWithoutBack();
$billingEntryLinks = [
'platform_orders' => \App\Support\BackUrl::withBack('/admin/platform-orders', $safeBackForLinks),
'site_subscriptions' => \App\Support\BackUrl::withBack('/admin/site-subscriptions', $safeBackForLinks),
'plans' => \App\Support\BackUrl::withBack('/admin/plans', $safeBackForLinks),
];
$platformOrdersQuickLinks = [
'unpaid_pending' => \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=unpaid&status=pending', $safeBackForLinks),
// 待生效paid + pending并显式锁定 sync_status=unsynced排除同步失败等异常单
'paid_pending' => \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=paid&status=pending&sync_status=unsynced', $safeBackForLinks),
'syncable_only' => \App\Support\BackUrl::withBack('/admin/platform-orders?syncable_only=1', $safeBackForLinks),
'sync_failed' => \App\Support\BackUrl::withBack('/admin/platform-orders?sync_status=failed', $safeBackForLinks),
];
@endphp
<div data-page="admin.dashboard">
<div class="page-header mb-20">
<div class="page-header-main">
<div>
<div class="page-header-title">总台仪表盘</div>
<div class="page-header-subtitle">欢迎回来,{{ $adminName }}。这里是总台管理(平台运营方)的全局总览入口(聚焦:收费闭环与治理动作可达)。</div>
</div>
<div class="page-header-actions">
<a class="btn btn-secondary btn-sm" href="#billing-workbench">跳到收费工作台</a>
</div>
</div>
<div class="page-header-meta">
<div>统计缓存:{{ $cacheMeta['store'] }} / TTL {{ $cacheMeta['ttl'] }}</div>
<div>当前视角:{{ $platformOverview['current_scope'] }}</div>
<div>活跃站点:{{ $platformOverview['active_merchants'] }}</div>
<div>待处理订单:{{ $platformOverview['pending_orders'] }}</div>
</div>
</div>
<div class="kpi-grid mb-20" data-role="kpi-grid">
@php
$kpiLinks = [
'merchants' => \App\Support\BackUrl::withBack('/admin/merchants', $selfWithoutBack),
'plans' => \App\Support\BackUrl::withBack('/admin/plans', $selfWithoutBack),
'site_subscriptions' => \App\Support\BackUrl::withBack('/admin/site-subscriptions', $selfWithoutBack),
'platform_orders' => \App\Support\BackUrl::withBack('/admin/platform-orders', $selfWithoutBack),
];
@endphp
<div class="card">
<div class="stat-card-title">站点</div>
<div class="stat-card-value"><a class="link" href="{!! $kpiLinks['merchants'] !!}">{{ $stats['merchants'] }}</a></div>
<div class="stat-card-footnote">全站点数量(总台视角)</div>
</div>
<div class="card">
<div class="stat-card-title">套餐</div>
<div class="stat-card-value"><a class="link" href="{!! $kpiLinks['plans'] !!}">{{ (int) ($stats['plans'] ?? 0) }}</a></div>
<div class="stat-card-footnote">可售套餐目录</div>
</div>
<div class="card">
<div class="stat-card-title">订阅</div>
<div class="stat-card-value"><a class="link" href="{!! $kpiLinks['site_subscriptions'] !!}">{{ (int) ($stats['site_subscriptions'] ?? 0) }}</a></div>
<div class="stat-card-footnote">站点订阅总量(收费主链)</div>
</div>
<div class="card">
<div class="stat-card-title">平台订单</div>
<div class="stat-card-value"><a class="link" href="{!! $kpiLinks['platform_orders'] !!}">{{ (int) ($stats['platform_orders'] ?? 0) }}</a></div>
<div class="stat-card-footnote">平台收费订单总量</div>
</div>
</div>
<div class="two-col mb-20" data-role="analysis-skeleton">
<div class="card">
<h3 class="mt-0">趋势</h3>
<div class="muted">近7天平台订单按天</div>
@php
$trendRows = (array) ($platformOrderTrend7d ?? []);
// 用于前端渐进增强渲染迷你图表JS 读取 data-points
$trendPoints = [];
foreach ($trendRows as $r) {
$trendPoints[] = [
'date' => (string) ($r['date'] ?? ''),
'count' => (int) ($r['count'] ?? 0),
'paid_sum' => (float) ($r['paid_sum'] ?? 0),
];
}
@endphp
<div class="adm-mini-chart" data-role="platform-order-trend-7d-chart" data-points='@json($trendPoints)'></div>
@php
$trendPaidTotal = 0.0;
$trendOrdersTotal = 0;
$trendPaidMax = 0.0;
foreach ($trendRows as $r) {
$trendPaidTotal += (float) ($r['paid_sum'] ?? 0);
$trendOrdersTotal += (int) ($r['count'] ?? 0);
$trendPaidMax = max($trendPaidMax, (float) ($r['paid_sum'] ?? 0));
}
@endphp
<div class="adm-mini-meta" data-role="platform-order-trend-7d-meta">
<span class="adm-mini-meta-item">7天合计已付:<strong>¥{{ number_format($trendPaidTotal, 2) }}</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">7天订单数:<strong>{{ (int) $trendOrdersTotal }}</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">峰值:<strong>¥{{ number_format($trendPaidMax, 2) }}</strong></span>
</div>
<table class="mt-10" data-role="platform-order-trend-7d">
<thead>
<tr>
<th>日期</th>
<th>订单数</th>
<th>已付金额</th>
</tr>
</thead>
<tbody>
@forelse($trendRows as $row)
<tr>
@php
$d = (string) ($row['date'] ?? '');
$dayOrdersUrl = \App\Support\BackUrl::withBack(
'/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'created_from' => $d,
'created_to' => $d,
]),
$selfWithoutBack
);
@endphp
<td><a class="link" href="{!! $dayOrdersUrl !!}">{{ $d }}</a></td>
<td>{{ (int) ($row['count'] ?? 0) }}</td>
<td>¥{{ number_format((float) ($row['paid_sum'] ?? 0), 2) }}</td>
</tr>
@empty
<tr>
<td colspan="3" class="muted">暂无数据</td>
</tr>
@endforelse
</tbody>
</table>
<div class="muted muted-xs mt-10">说明:先接入最小可用趋势数据;后续再补时间范围切换、维度切换与可视化图表。</div>
</div>
<div class="card">
<h3 class="mt-0">排行近7天站点收入 Top5</h3>
@php
$rankRows = (array) ($merchantRevenueRank7d ?? []);
$rankTotal = 0.0;
foreach ($rankRows as $r) {
$rankTotal += (float) ($r['paid_sum'] ?? 0);
}
@endphp
<div class="muted">近7天按已付金额统计</div>
@php
// 用于前端渐进增强渲染迷你排行JS 读取 data-points
$rankPoints = [];
foreach ($rankRows as $r) {
$mid = (int) ($r['merchant_id'] ?? 0);
$rankPoints[] = [
'merchant_id' => $mid,
'name' => (string) (($merchantIdToName[$mid] ?? '') ?: ('#' . $mid)),
'count' => (int) ($r['count'] ?? 0),
'paid_sum' => (float) ($r['paid_sum'] ?? 0),
];
}
@endphp
<div class="adm-mini-rank" data-role="merchant-revenue-rank-7d-chart" data-points='@json($rankPoints)'></div>
@php
$rankPaidMax = 0.0;
$rankOrdersTotal = 0;
foreach ($rankRows as $r) {
$rankPaidMax = max($rankPaidMax, (float) ($r['paid_sum'] ?? 0));
$rankOrdersTotal += (int) ($r['count'] ?? 0);
}
@endphp
<div class="adm-mini-meta" data-role="merchant-revenue-rank-7d-meta">
<span class="adm-mini-meta-item">Top5合计已付<strong>¥{{ number_format($rankTotal, 2) }}</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">Top5订单数<strong>{{ (int) $rankOrdersTotal }}</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">Top1金额<strong>¥{{ number_format($rankPaidMax, 2) }}</strong></span>
</div>
<table class="mt-10" data-role="merchant-revenue-rank-7d">
<thead>
<tr>
<th>站点</th>
<th>订单数</th>
<th>已付金额</th>
</tr>
</thead>
<tbody>
@forelse($rankRows as $row)
@php
$mid = (int) ($row['merchant_id'] ?? 0);
$mname = (string) (($merchantIdToName[$mid] ?? '') ?: ('#' . $mid));
$merchantOrdersUrl = \App\Support\BackUrl::withBack(
'/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'merchant_id' => $mid,
'created_from' => now()->subDays(6)->format('Y-m-d'),
'created_to' => now()->format('Y-m-d'),
]),
$selfWithoutBack
);
@endphp
<tr>
<td><a class="link" href="{!! $merchantOrdersUrl !!}">{{ $mname }}</a></td>
<td>{{ (int) ($row['count'] ?? 0) }}</td>
<td>¥{{ number_format((float) ($row['paid_sum'] ?? 0), 2) }}</td>
</tr>
@empty
<tr>
<td colspan="3" class="muted">暂无数据</td>
</tr>
@endforelse
</tbody>
</table>
<div class="muted muted-xs mt-10">说明:先落最小可用 Top5 排行;后续补时间范围切换、维度切换与异常排行。</div>
</div>
</div>
<div class="two-col mb-20">
<div class="card" id="billing-workbench">
<h3 class="mt-0">收费工作台(快捷治理)</h3>
<div class="muted">聚焦收费闭环的日常治理入口:订单 订阅 套餐。</div>
<div class="actions mt-10">
<a class="btn btn-sm" href="{!! $billingEntryLinks['platform_orders'] !!}">平台订单</a>
<a class="btn btn-secondary btn-sm" href="{!! $billingEntryLinks['site_subscriptions'] !!}">订阅</a>
<a class="btn btn-secondary btn-sm" href="{!! $billingEntryLinks['plans'] !!}">套餐</a>
</div>
<div class="mt-12">
<div class="muted">平台订单快捷筛选:</div>
<div class="actions mt-8">
<a class="btn btn-secondary btn-sm" href="{!! $platformOrdersQuickLinks['unpaid_pending'] !!}">待支付({{ (int) ($stats['platform_orders_unpaid_pending'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! $platformOrdersQuickLinks['paid_pending'] !!}">待生效({{ (int) ($stats['platform_orders_paid_pending'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! $platformOrdersQuickLinks['syncable_only'] !!}">可同步({{ (int) ($stats['platform_orders_syncable'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! $platformOrdersQuickLinks['sync_failed'] !!}">同步失败({{ (int) ($stats['platform_orders_sync_failed'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/platform-orders?renewal_missing_subscription=1', $selfWithoutBack) !!}">续费缺订阅({{ (int) ($stats['platform_orders_renewal_missing_subscription'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/platform-orders?bmpa_failed_only=1', $selfWithoutBack) !!}">BMPA失败{{ (int) ($stats['platform_orders_bmpa_failed'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=paid&receipt_status=none', $selfWithoutBack) !!}">无回执({{ (int) ($stats['platform_orders_paid_no_receipt'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/platform-orders?reconcile_mismatch=1', $selfWithoutBack) !!}">对账不一致({{ (int) ($stats['platform_orders_reconcile_mismatch'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/platform-orders?refund_inconsistent=1', $selfWithoutBack) !!}">退款不一致({{ (int) ($stats['platform_orders_refund_inconsistent'] ?? 0) }}</a>
</div>
</div>
<div class="mt-12">
<div class="muted">订阅到期治理:</div>
<div class="actions mt-8">
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/site-subscriptions?expiry=expiring_7d', $selfWithoutBack) !!}">7天内到期({{ (int) ($stats['site_subscriptions_expiring_7d'] ?? 0) }}</a>
<a class="btn btn-secondary btn-sm" href="{!! \App\Support\BackUrl::withBack('/admin/site-subscriptions?expiry=expired', $selfWithoutBack) !!}">已过期({{ (int) ($stats['site_subscriptions_expired'] ?? 0) }}</a>
</div>
</div>
<div class="muted muted-xs mt-10">说明:这里先把收费主链的高频治理入口收敛到仪表盘;后续再补趋势/排行的真实聚合。</div>
</div>
<div class="card">
<h3 class="mt-0">平台定位</h3>
<table>
<tr><th>后台角色</th><td>{{ $platformOverview['system_role'] }}</td></tr>
<tr><th>当前视角</th><td>{{ $platformOverview['current_scope'] }}</td></tr>
<tr><th>商家模式</th><td>{{ $platformOverview['merchant_mode'] }}</td></tr>
<tr><th>渠道数</th><td>{{ $platformOverview['channel_count'] }}</td></tr>
<tr><th>活跃商家</th><td>{{ $platformOverview['active_merchants'] }}</td></tr>
<tr><th>待处理订单</th><td>{{ $platformOverview['pending_orders'] }}</td></tr>
</table>
</div>
</div>
<div class="two-col mb-20" data-role="analysis-skeleton-row2">
<div class="card">
<div class="flex-between">
<h3 class="mt-0">最近平台订单</h3>
@php
$platformOrdersIndexUrl = \App\Support\BackUrl::withBack('/admin/platform-orders', $selfWithoutBack);
@endphp
<a class="muted" href="{!! $platformOrdersIndexUrl !!}">查看全部</a>
</div>
<table>
<thead>
<tr>
<th>订单号</th>
<th>类型</th>
<th>金额</th>
<th>支付</th>
<th>状态</th>
</tr>
</thead>
<tbody>
@forelse(($recentPlatformOrders ?? []) as $po)
@php
$poShowUrl = \App\Support\BackUrl::withBack('/admin/platform-orders/' . $po->id, $selfWithoutBack);
$hasReceiptEvidence = (data_get($po->meta, 'payment_summary.total_amount') !== null)
|| (data_get($po->meta, 'payment_receipts.0.amount') !== null);
$fixReceiptUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'add-payment-receipt');
$noReceiptListUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=paid&receipt_status=none', $selfWithoutBack);
$syncErrMsg = (string) (data_get($po->meta, 'subscription_activation_error.message') ?? '');
$bmpaErrMsg = (string) (data_get($po->meta, 'batch_mark_paid_and_activate_error.message') ?? '');
$syncFailedListUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?sync_status=failed', $selfWithoutBack);
$bmpaFailedListUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?bmpa_failed_only=1', $selfWithoutBack);
// 运营提效:失败原因较短时,提供“一键进入同原因集合”链接(避免复制粘贴)。
$FAILED_REASON_KEYWORD_MAX_LEN = 80;
$syncReasonUrl = '';
if ($syncErrMsg !== '' && mb_strlen($syncErrMsg) <= $FAILED_REASON_KEYWORD_MAX_LEN) {
$syncReasonUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'sync_status' => 'failed',
'sync_error_keyword' => $syncErrMsg,
]), $selfWithoutBack);
}
$bmpaReasonUrl = '';
if ($bmpaErrMsg !== '' && mb_strlen($bmpaErrMsg) <= $FAILED_REASON_KEYWORD_MAX_LEN) {
$bmpaReasonUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'bmpa_failed_only' => '1',
'bmpa_error_keyword' => $bmpaErrMsg,
]), $selfWithoutBack);
}
@endphp
<tr data-role="recent-platform-order-row" data-order-no="{{ $po->order_no }}">
<td><a class="link" href="{!! $poShowUrl !!}">{{ $po->order_no }}</a></td>
<td>{{ $po->orderTypeLabel() }}</td>
<td>¥{{ number_format((float) $po->payable_amount, 2) }}</td>
<td>
{{ $po->payment_status }}
@if((string) $po->payment_status === 'paid' && ! $hasReceiptEvidence)
<div class="muted text-danger muted-xs row-warn" data-role="recent-order-no-receipt-hint" title="已付 ¥{{ number_format((float) $po->paid_amount, 2) }}|无回执证据">
<span class="row-warn-prefix">无回执</span>
<span class="muted"></span>
<a class="link" href="{!! $fixReceiptUrl !!}">去补回执</a>
<span class="muted"></span>
<a class="link" href="{!! $noReceiptListUrl !!}">进入集合</a>
</div>
@endif
</td>
<td>
{{ $po->status }}
@if($syncErrMsg !== '')
<div class="muted text-danger muted-xs row-warn" data-role="recent-order-sync-failed-hint" title="{{ e($syncErrMsg) }}">
<span class="row-warn-prefix">同步失败</span>
<span class="muted"></span>
<a class="link" href="{!! $syncFailedListUrl !!}">进入集合</a>
@if($syncReasonUrl !== '')
<span class="muted"></span>
<a class="link" href="{!! $syncReasonUrl !!}">同原因集合</a>
@endif
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'sync-failed') !!}">查看失败详情</a>
</div>
@endif
@if($bmpaErrMsg !== '')
<div class="muted text-danger muted-xs row-warn" data-role="recent-order-bmpa-failed-hint" title="{{ e($bmpaErrMsg) }}">
<span class="row-warn-prefix">BMPA失败</span>
<span class="muted"></span>
<a class="link" href="{!! $bmpaFailedListUrl !!}">进入集合</a>
@if($bmpaReasonUrl !== '')
<span class="muted"></span>
<a class="link" href="{!! $bmpaReasonUrl !!}">同原因集合</a>
@endif
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'bmpa-failed') !!}">查看失败详情</a>
</div>
@endif
@if((string) $po->status === 'pending' && (string) $po->payment_status === 'paid' && $po->isReconcileMismatch())
@php
$reconcileMismatchUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?reconcile_mismatch=1', $selfWithoutBack);
@endphp
<div class="muted text-danger muted-xs row-warn" data-role="recent-order-reconcile-mismatch-hint" title="已付 ¥{{ number_format((float) $po->paid_amount, 2) }}|回执 ¥{{ number_format((float) $po->receiptTotal(), 2) }}">
<span class="row-warn-prefix">对账不一致</span>
<span class="muted"></span>
<a class="link" href="{!! $reconcileMismatchUrl !!}">进入集合</a>
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'payment-receipts') !!}">去对账</a>
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'add-payment-receipt') !!}">去补回执</a>
</div>
@endif
@if($po->isRefundInconsistent())
@php
$refundInconsistentUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?refund_inconsistent=1', $selfWithoutBack);
@endphp
<div class="muted text-danger muted-xs row-warn" data-role="recent-order-refund-inconsistent-hint" title="已付 ¥{{ number_format((float) $po->paid_amount, 2) }}|退款 ¥{{ number_format((float) $po->refundTotal(), 2) }}">
<span class="row-warn-prefix">退款不一致</span>
<span class="muted"></span>
<a class="link" href="{!! $refundInconsistentUrl !!}">进入集合</a>
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'refund-receipts') !!}">去核对退款</a>
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'add-refund-receipt') !!}">去补退款记录</a>
</div>
@endif
@if((string) $po->order_type === 'renewal' && empty($po->site_subscription_id))
@php
$renewalMissingSubscriptionUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?renewal_missing_subscription=1', $selfWithoutBack);
@endphp
<div class="muted text-danger muted-xs row-warn" data-role="recent-order-renewal-missing-subscription-hint" title="站点 #{{ (int) ($po->merchant_id ?? 0) }}|套餐 #{{ (int) ($po->plan_id ?? 0) }}site_subscription_id 为空">
<span class="row-warn-prefix">续费缺订阅</span>
<span class="muted"></span>
<a class="link" href="{!! $renewalMissingSubscriptionUrl !!}">进入集合</a>
<span class="muted"></span>
<a class="link" href="{!! \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'relation-subscription') !!}">去关联订阅</a>
</div>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="5" class="muted">暂无数据</td>
</tr>
@endforelse
</tbody>
</table>
<div class="muted muted-xs mt-10">说明:当前先接入最近订单列表;后续补“同步状态/站点/套餐/治理入口”。</div>
</div>
<div class="card">
<div class="flex-between">
<h3 class="mt-0">套餐订单占比Top5</h3>
@php
$plansIndexUrl = \App\Support\BackUrl::withBack('/admin/plans', $selfWithoutBack);
@endphp
<a class="muted" href="{!! $plansIndexUrl !!}">查看套餐</a>
</div>
<div class="muted">近7天按订单数统计</div>
@php
$shareRows = (array) ($planOrderShare ?? []);
$top5Orders = 0;
foreach ($shareRows as $r) {
$top5Orders += (int) ($r['count'] ?? 0);
}
$totalOrders = (int) ($planOrderShareTotal ?? 0);
if ($totalOrders <= 0) {
// 兜底:兼容旧数据(未传 total 时,至少不影响渲染)
$totalOrders = (int) $top5Orders;
}
@endphp
@php
// 用于前端渐进增强渲染占比条形图JS 读取 data-points
$sharePoints = [];
foreach ($shareRows as $r) {
$pid = (int) ($r['plan_id'] ?? 0);
$sharePoints[] = [
'plan_id' => $pid,
'name' => (string) (($planIdToName[$pid] ?? '') ?: ('#' . $pid)),
'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>
@php
$shareTop1Count = 0;
foreach ($shareRows as $r) {
$shareTop1Count = max($shareTop1Count, (int) ($r['count'] ?? 0));
}
$shareTop1Pct = $totalOrders > 0 ? round(($shareTop1Count / $totalOrders) * 100, 1) : 0;
$shareCoveragePct = $totalOrders > 0 ? round(($top5Orders / $totalOrders) * 100, 1) : 0;
$shareOtherPct = max(0, round(100 - $shareCoveragePct, 1));
$shareOtherCount = max(0, (int) $totalOrders - (int) $top5Orders);
@endphp
<div class="adm-mini-meta" data-role="plan-order-share-top5-meta">
<span class="adm-mini-meta-item">全量订单:<strong>{{ (int) $totalOrders }}</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">Top5合计<strong>{{ (int) $top5Orders }}</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">覆盖率:<strong>{{ $shareCoveragePct }}%</strong></span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">其它:<strong>{{ $shareOtherPct }}%</strong>{{ (int) $shareOtherCount }}单)</span>
<span class="adm-mini-meta-sep"></span>
<span class="adm-mini-meta-item">Top1占比<strong>{{ $shareTop1Pct }}%</strong></span>
</div>
<table>
<thead>
<tr>
<th>套餐</th>
<th>订单数</th>
<th>占比</th>
</tr>
</thead>
<tbody>
@forelse($shareRows as $row)
@php
$planId = (int) ($row['plan_id'] ?? 0);
$count = (int) ($row['count'] ?? 0);
$pct = $totalOrders > 0 ? round(($count / $totalOrders) * 100, 1) : 0;
$planName = (string) (($planIdToName[$planId] ?? '') ?: ('#' . $planId));
@endphp
@php
$planOrdersUrl = \App\Support\BackUrl::withBack(
'/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'plan_id' => $planId,
'created_from' => now()->subDays(6)->format('Y-m-d'),
'created_to' => now()->format('Y-m-d'),
]),
$selfWithoutBack
);
@endphp
<tr>
<td><a class="link" href="{!! $planOrdersUrl !!}">{{ $planName }}</a></td>
<td>{{ $count }}</td>
<td>{{ $pct }}%</td>
</tr>
@empty
<tr>
<td colspan="3" class="muted">暂无数据</td>
</tr>
@endforelse
</tbody>
</table>
<div class="muted muted-xs mt-10">说明:当前口径为“平台订单按 plan_id 的数量占比Top5后续扩展到金额占比、渠道占比与时间范围切换。</div>
</div>
</div>
</div>
@endsection