kpi.blade.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. @extends('base.base')
  2. @section('base')
  3. @php
  4. $reviewingCount = (int)($kpi['reviewing_count'] ?? 0);
  5. $approvedCount = (int)($kpi['approved_count'] ?? 0);
  6. $rejectedCount = (int)($kpi['rejected_count'] ?? 0);
  7. $paidLiability = (int)($kpi['paid_liability'] ?? 0);
  8. $decisionCount = $approvedCount + $rejectedCount;
  9. $approvalRate = $decisionCount > 0 ? round($approvedCount * 100 / $decisionCount, 1) : 0;
  10. $rejectedRate = $decisionCount > 0 ? round($rejectedCount * 100 / $decisionCount, 1) : 0;
  11. $totalCount = $reviewingCount + $decisionCount;
  12. $reviewingRate = $totalCount > 0 ? round($reviewingCount * 100 / $totalCount, 1) : 0;
  13. @endphp
  14. <style>
  15. .world-cup-kpi-band {
  16. display: flex;
  17. align-items: center;
  18. justify-content: space-between;
  19. gap: 12px;
  20. }
  21. .world-cup-kpi-value {
  22. font-size: 28px;
  23. line-height: 1.1;
  24. font-weight: 600;
  25. }
  26. .world-cup-kpi-label {
  27. margin-bottom: 10px;
  28. font-size: 14px;
  29. opacity: 0.9;
  30. }
  31. .world-cup-progress {
  32. height: 10px;
  33. border-radius: 6px;
  34. background: #edf2f7;
  35. overflow: hidden;
  36. }
  37. .world-cup-progress span {
  38. display: block;
  39. height: 100%;
  40. }
  41. .world-cup-section-action {
  42. display: flex;
  43. justify-content: space-between;
  44. align-items: center;
  45. gap: 12px;
  46. margin-bottom: 16px;
  47. }
  48. @media screen and (max-width: 767px) {
  49. .world-cup-section-action {
  50. align-items: flex-start;
  51. flex-direction: column;
  52. }
  53. }
  54. </style>
  55. <div class="main-panel">
  56. <div class="content-wrapper">
  57. <div class="page-header">
  58. <h3 class="page-title">
  59. <span class="page-title-icon bg-gradient-primary text-white mr-2">
  60. <i class="mdi mdi-chart-bar"></i>
  61. </span>
  62. World Cup KPI 看板
  63. </h3>
  64. <nav aria-label="breadcrumb">
  65. <ol class="breadcrumb">
  66. <li class="breadcrumb-item"><a href="#">活动管理</a></li>
  67. <li class="breadcrumb-item active" aria-current="page">KPI 看板</li>
  68. </ol>
  69. </nav>
  70. </div>
  71. <div class="row">
  72. <div class="col-md-3 stretch-card grid-margin">
  73. <div class="card bg-gradient-warning text-white">
  74. <div class="card-body">
  75. <div class="world-cup-kpi-band">
  76. <div>
  77. <div class="world-cup-kpi-label">待审核奖励</div>
  78. <div class="world-cup-kpi-value">{{ $reviewingCount }}</div>
  79. </div>
  80. <i class="mdi mdi-timer-sand" style="font-size: 34px;"></i>
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. <div class="col-md-3 stretch-card grid-margin">
  86. <div class="card bg-gradient-success text-white">
  87. <div class="card-body">
  88. <div class="world-cup-kpi-band">
  89. <div>
  90. <div class="world-cup-kpi-label">已通过奖励</div>
  91. <div class="world-cup-kpi-value">{{ $approvedCount }}</div>
  92. </div>
  93. <i class="mdi mdi-check-circle-outline" style="font-size: 34px;"></i>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. <div class="col-md-3 stretch-card grid-margin">
  99. <div class="card bg-gradient-danger text-white">
  100. <div class="card-body">
  101. <div class="world-cup-kpi-band">
  102. <div>
  103. <div class="world-cup-kpi-label">已驳回奖励</div>
  104. <div class="world-cup-kpi-value">{{ $rejectedCount }}</div>
  105. </div>
  106. <i class="mdi mdi-close-circle-outline" style="font-size: 34px;"></i>
  107. </div>
  108. </div>
  109. </div>
  110. </div>
  111. <div class="col-md-3 stretch-card grid-margin">
  112. <div class="card bg-gradient-info text-white">
  113. <div class="card-body">
  114. <div class="world-cup-kpi-band">
  115. <div>
  116. <div class="world-cup-kpi-label">已赔付金额</div>
  117. <div class="world-cup-kpi-value">${{ number_format($paidLiability / 100, 2) }}</div>
  118. </div>
  119. <i class="mdi mdi-cash-multiple" style="font-size: 34px;"></i>
  120. </div>
  121. </div>
  122. </div>
  123. </div>
  124. </div>
  125. <div class="row">
  126. <div class="col-lg-5 grid-margin stretch-card">
  127. <div class="card">
  128. <div class="card-body">
  129. <div class="world-cup-section-action">
  130. <h4 class="card-title mb-0">审核漏斗</h4>
  131. <div>
  132. <a class="btn btn-gradient-primary btn-sm" href="/admin/world-cup/rewards">进入审核队列</a>
  133. <a class="btn btn-outline-primary btn-sm" href="/admin/world-cup/schedule">赛程更新</a>
  134. </div>
  135. </div>
  136. <div class="mb-4">
  137. <div class="d-flex justify-content-between mb-2">
  138. <span>待审核占比</span>
  139. <span>{{ $reviewingRate }}%</span>
  140. </div>
  141. <div class="world-cup-progress">
  142. <span style="width: {{ $reviewingRate }}%; background: #fed713;"></span>
  143. </div>
  144. </div>
  145. <div class="mb-4">
  146. <div class="d-flex justify-content-between mb-2">
  147. <span>通过率</span>
  148. <span>{{ $approvalRate }}%</span>
  149. </div>
  150. <div class="world-cup-progress">
  151. <span style="width: {{ $approvalRate }}%; background: #1bcfb4;"></span>
  152. </div>
  153. </div>
  154. <div>
  155. <div class="d-flex justify-content-between mb-2">
  156. <span>驳回率</span>
  157. <span>{{ $rejectedRate }}%</span>
  158. </div>
  159. <div class="world-cup-progress">
  160. <span style="width: {{ $rejectedRate }}%; background: #fe7c96;"></span>
  161. </div>
  162. </div>
  163. <div class="table-responsive mt-4">
  164. <table class="table table-bordered">
  165. <tbody>
  166. <tr>
  167. <th>已处理总数</th>
  168. <td>{{ $decisionCount }}</td>
  169. </tr>
  170. <tr>
  171. <th>当前队列总数</th>
  172. <td>{{ $totalCount }}</td>
  173. </tr>
  174. <tr>
  175. <th>已赔付总额</th>
  176. <td>${{ number_format($paidLiability / 100, 2) }}</td>
  177. </tr>
  178. </tbody>
  179. </table>
  180. </div>
  181. </div>
  182. </div>
  183. </div>
  184. <div class="col-lg-7 grid-margin stretch-card">
  185. <div class="card">
  186. <div class="card-body">
  187. <div class="world-cup-section-action">
  188. <h4 class="card-title mb-0">High Risk 待审</h4>
  189. <a class="btn btn-outline-danger btn-sm" href="/admin/world-cup/rewards?status=reviewing&risk=high">只看高风险</a>
  190. </div>
  191. <div class="table-responsive">
  192. <table class="table table-bordered table-hover">
  193. <thead>
  194. <tr>
  195. <th>Reward ID</th>
  196. <th>邀请人</th>
  197. <th>被邀请人</th>
  198. <th>首充</th>
  199. <th>赔付</th>
  200. <th>风险</th>
  201. <th>提交时间</th>
  202. </tr>
  203. </thead>
  204. <tbody>
  205. @forelse($highRiskList as $row)
  206. <tr>
  207. <td>{{ $row['reward_id'] }}</td>
  208. <td>{{ $row['referrer_id'] }}</td>
  209. <td>{{ $row['invitee_id'] }}</td>
  210. <td>${{ number_format(($row['first_deposit_amt'] ?? 0) / 100, 2) }}</td>
  211. <td>${{ number_format(($row['total_liability'] ?? 0) / 100, 2) }}</td>
  212. <td>
  213. <span class="badge badge-danger">
  214. {{ $row['risk_level'] ?? 'high' }} / {{ $row['risk_score'] ?? 0 }}
  215. </span>
  216. </td>
  217. <td>{{ $row['submitted_at'] ?? '-' }}</td>
  218. </tr>
  219. @empty
  220. <tr>
  221. <td colspan="7" class="text-center text-muted">暂无 High Risk 待审奖励</td>
  222. </tr>
  223. @endforelse
  224. </tbody>
  225. </table>
  226. </div>
  227. </div>
  228. </div>
  229. </div>
  230. </div>
  231. <div class="row">
  232. <div class="col-lg-6 grid-margin stretch-card">
  233. <div class="card">
  234. <div class="card-body">
  235. <h4 class="card-title">最新待审核</h4>
  236. <div class="table-responsive">
  237. <table class="table table-bordered table-hover">
  238. <thead>
  239. <tr>
  240. <th>Reward ID</th>
  241. <th>邀请人</th>
  242. <th>被邀请人</th>
  243. <th>风险</th>
  244. <th>提交时间</th>
  245. </tr>
  246. </thead>
  247. <tbody>
  248. @forelse($reviewingList as $row)
  249. <tr>
  250. <td>{{ $row['reward_id'] }}</td>
  251. <td>{{ $row['referrer_id'] }}</td>
  252. <td>{{ $row['invitee_id'] }}</td>
  253. <td>{{ $row['risk_level'] ?? '-' }}</td>
  254. <td>{{ $row['submitted_at'] ?? '-' }}</td>
  255. </tr>
  256. @empty
  257. <tr>
  258. <td colspan="5" class="text-center text-muted">暂无待审核奖励</td>
  259. </tr>
  260. @endforelse
  261. </tbody>
  262. </table>
  263. </div>
  264. </div>
  265. </div>
  266. </div>
  267. <div class="col-lg-6 grid-margin stretch-card">
  268. <div class="card">
  269. <div class="card-body">
  270. <div class="world-cup-section-action">
  271. <h4 class="card-title mb-0">最近审核日志</h4>
  272. <a class="btn btn-outline-primary btn-sm" href="/admin/world-cup/logs">查看全部日志</a>
  273. </div>
  274. <div class="table-responsive">
  275. <table class="table table-bordered table-hover">
  276. <thead>
  277. <tr>
  278. <th>Reward ID</th>
  279. <th>操作人</th>
  280. <th>动作</th>
  281. <th>状态变化</th>
  282. <th>时间</th>
  283. </tr>
  284. </thead>
  285. <tbody>
  286. @forelse($logs as $log)
  287. <tr>
  288. <td>{{ $log['reward_id'] ?? '-' }}</td>
  289. <td>{{ $log['actor'] ?? '-' }}</td>
  290. <td>{{ $log['action'] ?? '-' }}</td>
  291. <td>{{ $log['before_status'] ?? '-' }} -> {{ $log['after_status'] ?? '-' }}</td>
  292. <td>{{ $log['created_at'] ?? '-' }}</td>
  293. </tr>
  294. @empty
  295. <tr>
  296. <td colspan="5" class="text-center text-muted">暂无审核日志</td>
  297. </tr>
  298. @endforelse
  299. </tbody>
  300. </table>
  301. </div>
  302. </div>
  303. </div>
  304. </div>
  305. </div>
  306. </div>
  307. </div>
  308. @endsection