userDataAnalysis.blade.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. @extends('admin.layouts')
  2. @section('content')
  3. <div class="page-content container">
  4. <div class="card card-shadow">
  5. <div class="card-block p-30">
  6. <form class="form-row">
  7. <div class="form-group col-xxl-1 col-lg-1 col-md-1 col-sm-4">
  8. <input class="form-control" name="uid" type="number" value="{{ Request::query('uid') }}" placeholder="{{ trans('model.user.id') }}" />
  9. </div>
  10. <div class="form-group col-xxl-2 col-lg-3 col-md-3 col-sm-4">
  11. <input class="form-control" name="username" type="text" value="{{ Request::query('username') }}"
  12. placeholder="{{ trans('model.user.username') }}" />
  13. </div>
  14. <div class="form-group col-xxl-1 col-lg-3 col-md-3 col-4 btn-group">
  15. <button class="btn btn-primary" type="submit">{{ trans('common.search') }}</button>
  16. <a class="btn btn-danger" href="{{ route('admin.report.userAnalysis') }}">{{ trans('common.reset') }}</a>
  17. </div>
  18. </form>
  19. </div>
  20. </div>
  21. @isset($data)
  22. <div class="card card-shadow">
  23. <div class="card-block p-30">
  24. <div class="row pb-20">
  25. <div class="col-md-8 col-sm-6">
  26. <div class="blue-grey-700 font-size-26 font-weight-500">{{ trans('admin.report.hourly_traffic') }}</div>
  27. </div>
  28. </div>
  29. <canvas id="hourlyBar"></canvas>
  30. <canvas id="hourlyDoughnut"></canvas>
  31. </div>
  32. </div>
  33. <div class="card card-shadow">
  34. <div class="card-block p-30">
  35. <div class="row pb-20">
  36. <div class="col-md-8 col-sm-6">
  37. <div class="blue-grey-700 font-size-26 font-weight-500">{{ trans('admin.report.daily_traffic') }}</div>
  38. </div>
  39. </div>
  40. <canvas id="dailyBar"></canvas>
  41. <canvas id="dailyDoughnut"></canvas>
  42. </div>
  43. </div>
  44. @endisset
  45. </div>
  46. @endsection
  47. @section('javascript')
  48. @isset($data)
  49. <script src="/assets/global/vendor/chart-js/chart.min.js"></script>
  50. <script type="text/javascript">
  51. function createBarChart(elementId, labels, datasets, labelTail) {
  52. new Chart(document.getElementById(elementId), {
  53. type: 'bar',
  54. data: {
  55. labels: labels,
  56. datasets: datasets,
  57. },
  58. options: {
  59. parsing: {
  60. xAxisKey: 'time',
  61. yAxisKey: 'total',
  62. },
  63. scales: {
  64. x: {
  65. stacked: true,
  66. },
  67. y: {
  68. stacked: true,
  69. },
  70. },
  71. responsive: true,
  72. plugins: {
  73. legend: {
  74. labels: {
  75. padding: 20,
  76. usePointStyle: true,
  77. pointStyle: 'circle',
  78. font: {
  79. size: 14
  80. },
  81. },
  82. },
  83. tooltip: label_callbacks(labelTail),
  84. },
  85. },
  86. });
  87. }
  88. function label_callbacks(tail) {
  89. return {
  90. mode: 'index',
  91. intersect: false,
  92. callbacks: {
  93. title: function(context) {
  94. return context[0].label + ' ' + tail;
  95. },
  96. label: function(context) {
  97. let label = context.dataset.label || '';
  98. if (label) {
  99. label += ': ';
  100. }
  101. if (context.parsed.y !== null) {
  102. label += context.parsed.y + ' MiB';
  103. }
  104. return label;
  105. },
  106. },
  107. };
  108. }
  109. const userData = @json($data);
  110. const nodeColorMap = generateNodeColorMap(userData.nodes); // 获取所有节点名称并生成颜色映射
  111. function generateNodeColorMap(nodeNames) {
  112. const colorMap = {};
  113. Object.entries(nodeNames).forEach(([id, name]) => {
  114. colorMap[id] = getRandomColor(name);
  115. });
  116. return colorMap;
  117. }
  118. // 生成随机颜色
  119. function getRandomColor(name) {
  120. // 将字符串转换为哈希值
  121. let hash = 0;
  122. for (let i = 0; i < name.length; i++) {
  123. hash = name.charCodeAt(i) + ((hash << 5) - hash);
  124. }
  125. // 定义不同色调的范围
  126. const hueOffset = hash % 360;
  127. const hueRange = 20; // 色调范围
  128. // 计算最终色调
  129. const hue = (hueOffset + Math.random() * hueRange) % 360; // 确保 hue 在 0-359 之间
  130. // 保持饱和度和亮度固定
  131. const saturation = 70; // 保持饱和度较高
  132. const lightness = 50; // 保持亮度适中
  133. // 添加透明度
  134. const alpha = 0.55; // 50% 透明度
  135. return `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;
  136. }
  137. // 生成数据集
  138. // 生成数据集
  139. function generateDatasets(flows) {
  140. const dataByNode = {};
  141. // 按节点 ID 分组数据
  142. flows.forEach(flow => {
  143. if (!dataByNode[flow.id]) {
  144. dataByNode[flow.id] = [];
  145. }
  146. dataByNode[flow.id].push({
  147. time: flow.time,
  148. total: flow.total,
  149. name: flow.name,
  150. });
  151. });
  152. // 创建 datasets 数组
  153. let datasets = [];
  154. for (const nodeId in dataByNode) {
  155. if (dataByNode.hasOwnProperty(nodeId)) {
  156. datasets.push({
  157. label: dataByNode[nodeId][0].name, // 使用 name 作为标签
  158. backgroundColor: nodeColorMap[nodeId],
  159. borderColor: nodeColorMap[nodeId],
  160. data: dataByNode[nodeId],
  161. fill: true,
  162. });
  163. }
  164. }
  165. return datasets;
  166. }
  167. // 创建图表
  168. createBarChart('hourlyBar', userData.hours, generateDatasets(userData.hourlyFlows), @json(trans_choice('common.hour', 2)));
  169. createBarChart('dailyBar', userData.days, generateDatasets(userData.dailyFlows), @json(trans_choice('common.days.attribute', 2)));
  170. </script>
  171. @endisset
  172. @endsection