nodeList.blade.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. @extends('user.layouts')
  2. @section('css')
  3. <link href="/assets/global/fonts/font-awesome/css/all.min.css" rel="stylesheet">
  4. <link href="/assets/global/vendor/webui-popover/webui-popover.min.css" type="text/css" rel="stylesheet">
  5. <link href="/assets/global/vendor/jvectormap/jquery-jvectormap.min.css" type="text/css" rel="stylesheet">
  6. <style>
  7. .flag-icon-rounded {
  8. border-radius: 50%;
  9. background-size: cover;
  10. height: 100%;
  11. width: auto;
  12. aspect-ratio: 1 / 1;
  13. }
  14. </style>
  15. @endsection
  16. @section('content')
  17. <!-- BEGIN CONTENT BODY -->
  18. <div class="page-content container-fluid">
  19. <div class="row">
  20. <div class="col-md-9">
  21. <div class="card card-inverse card-shadow bg-white map">
  22. <div class="card-block h-450">
  23. <div class="h-p100" id="world-map"></div>
  24. </div>
  25. </div>
  26. </div>
  27. <div class="col-md-3">
  28. <div class="row map">
  29. <div class="col-md-12">
  30. <div class="card card-block p-20 bg-indigo-500">
  31. <div class="counter counter-lg counter-inverse">
  32. <div class="counter-label text-uppercase font-size-16">{{trans('user.account.level')}}</div>
  33. <div class="counter-number-group">
  34. <span class="counter-icon"><i class="icon wb-user-circle" aria-hidden="true"></i></span>
  35. <span class="counter-number ml-10">{{Auth::getUser()->level}}</span>
  36. </div>
  37. <div class="counter-label text-uppercase font-size-16">{{Auth::getUser()->level_name}}</div>
  38. </div>
  39. </div>
  40. </div>
  41. @if(Auth::getUser()->user_group_id)
  42. <div class="col-md-12">
  43. <div class="card card-block p-20 bg-indigo-500">
  44. <div class="counter counter-lg counter-inverse">
  45. <div class="counter-label text-uppercase font-size-16">{{trans('user.account.group')}}</div>
  46. <div class="counter-number-group">
  47. <span class="counter-icon"><i class="icon wb-globe" aria-hidden="true"></i></span>
  48. <span class="counter-number ml-10">{{Auth::getUser()->userGroup->name}}</span>
  49. </div>
  50. </div>
  51. </div>
  52. </div>
  53. @endif
  54. <div class="col-md-12">
  55. <div class="card card-block p-20 bg-indigo-500">
  56. <div class="counter counter-lg counter-inverse">
  57. <div class="counter-label text-uppercase font-size-16">{{trans('user.account.speed_limit')}}</div>
  58. <div class="counter-number-group">
  59. <span class="counter-icon"><i class="icon wb-signal" aria-hidden="true"></i></span>
  60. <span class="counter-number ml-10">{{Auth::getUser()->speed_limit ?? trans('common.unlimited')}}</span>
  61. </div>
  62. <div class="counter-label font-size-16">Mbps</div>
  63. </div>
  64. </div>
  65. </div>
  66. </div>
  67. </div>
  68. @foreach($nodeList as $node)
  69. <div class="col-xxl-3 col-xl-4 col-sm-6">
  70. <div class="card card-inverse card-shadow bg-white node">
  71. <div class="card-block p-30 row">
  72. <div class="col-4">
  73. <i class="fi fi-{{$node->country_code}} flag-icon-rounded" aria-hidden="true"></i>
  74. </div>
  75. <div class="col-8 text-break text-right">
  76. <p class="font-size-20 blue-600">
  77. <span class="float-left badge badge-round badge-default">{{$node->level_table->name}}</span>
  78. @if($node->offline && !$node->relay_node_id)
  79. <i class="red-600 icon wb-warning" data-content="{{trans('user.node.unstable')}}" data-trigger="hover" data-toggle="popover" data-placement="top"></i>
  80. @endif
  81. @if($node->traffic_rate !== 1.0)
  82. <i class="green-600 icon wb-info-circle" data-content="{{trans('user.node.rate', ['ratio' => $node->traffic_rate])}}" data-trigger="hover" data-toggle="popover" data-placement="top"></i>
  83. @endif
  84. {{$node->name}}
  85. </p>
  86. <blockquote>
  87. @foreach($node->labels as $label)
  88. <span class="badge badge-round badge-info">{{$label->name}}</span>
  89. @endforeach
  90. <br>
  91. {{$node->description}}
  92. </blockquote>
  93. <p class="font-size-14">
  94. <button class="btn btn-sm btn-outline-info" onclick="getInfo('{{$node->id}}','code')">
  95. <i id="code{{$node->id}}" class="fa-solid fa-code"></i>
  96. </button>
  97. <button class="btn btn-sm btn-outline-info" onclick="getInfo('{{$node->id}}','qrcode')">
  98. <i id="qrcode{{$node->id}}" class="fa-solid fa-qrcode"></i>
  99. </button>
  100. <button class="btn btn-sm btn-outline-info" onclick="getInfo('{{$node->id}}','text')">
  101. <i id="text{{$node->id}}" class="fa-solid fa-list"></i>
  102. </button>
  103. </p>
  104. </div>
  105. </div>
  106. </div>
  107. </div>
  108. @endforeach
  109. </div>
  110. </div>
  111. @endsection
  112. @section('javascript')
  113. <script src="/assets/global/vendor/matchheight/jquery.matchHeight-min.js" type="text/javascript"></script>
  114. <script src="/assets/global/js/Plugin/matchheight.js" type="text/javascript"></script>
  115. <script src="/assets/custom/easy.qrcode.min.js" type="text/javascript"></script>
  116. <script src="/assets/global/js/Plugin/webui-popover.js" type="text/javascript"></script>
  117. <script src="/assets/global/vendor/jvectormap/jquery-jvectormap.min.js"></script>
  118. <script src="/assets/custom/maps/jquery-jvectormap-world-mill-cn.js"></script>
  119. <script type="text/javascript">
  120. $(function() {
  121. $('#world-map').vectorMap({
  122. map: 'world_mill',
  123. scaleColors: ['#C8EEFF', '#0071A4'],
  124. normalizeFunction: 'polynomial',
  125. zoomAnimate: true,
  126. hoverOpacity: 0.7,
  127. hoverColor: false,
  128. regionStyle: {
  129. initial: {
  130. fill: '#3E8EF7',
  131. },
  132. hover: {
  133. fill: '#589FFC',
  134. },
  135. selected: {
  136. fill: '#0B69E3',
  137. },
  138. selectedHover: {
  139. fill: '#589FFC',
  140. },
  141. },
  142. markerStyle: {
  143. initial: {
  144. r: 3,
  145. fill: '#FF4C52',
  146. 'stroke-width': 0,
  147. },
  148. hover: {
  149. r: 6,
  150. stroke: '#FF4C52',
  151. 'stroke-width': 0,
  152. },
  153. },
  154. backgroundColor: '#fff',
  155. markers: [
  156. @foreach($nodesGeo as $name => $geo)
  157. {
  158. latLng: [{{$name}}], name: '{{$geo}}',
  159. },
  160. @endforeach
  161. ],
  162. });
  163. $('.node').matchHeight();
  164. $('.map').matchHeight();
  165. });
  166. function getInfo(id, type) {
  167. const oldClass = $('#' + type + id).attr('class');
  168. $.ajax({
  169. method: 'POST',
  170. url: '{{route('node')}}',
  171. data: {_token: '{{csrf_token()}}', id: id, type: type},
  172. beforeSend: function() {
  173. $('#' + type + id).removeAttr('class').addClass('icon wb-loop icon-spin');
  174. },
  175. success: function(ret) {
  176. if (ret.status === 'success') {
  177. switch (type) {
  178. case 'code':
  179. swal.fire({
  180. html: '<textarea class="form-control" rows="8" readonly="readonly">' + ret.data + '</textarea>' +
  181. '<a href="' + ret.data + '" class="btn btn-block btn-danger mt-4">{{trans('common.open')}}' +
  182. ret.title + '</a>',
  183. showConfirmButton: false,
  184. });
  185. break;
  186. case 'qrcode':
  187. swal.fire({
  188. title: '{{trans('user.scan_qrcode')}}',
  189. html: '<div id="qrcode"></div><button class="btn btn-block btn-outline-primary mt-4" onclick="Download()"> <i class="icon wb-download"></i> {{trans('common.download')}}</button>',
  190. onBeforeOpen: () => {
  191. new QRCode(document.getElementById('qrcode'), {text: ret.data});
  192. },
  193. showConfirmButton: false,
  194. });
  195. break;
  196. case 'text':
  197. swal.fire({
  198. title: '{{trans('user.node.info')}}',
  199. html: '<textarea class="form-control" rows="12" readonly="readonly">' + ret.data + '</textarea>',
  200. showConfirmButton: false,
  201. });
  202. break;
  203. default:
  204. swal.fire({title: ret.title, text: ret.data, icon: 'error'});
  205. }
  206. }
  207. },
  208. complete: function() {
  209. $('#' + type + id).removeAttr('class').addClass(oldClass);
  210. },
  211. });
  212. }
  213. function Download() {
  214. const canvas = document.getElementsByTagName('canvas')[0];
  215. canvas.toBlob((blob) => {
  216. let link = document.createElement('a');
  217. link.download = 'qr.png';
  218. let reader = new FileReader();
  219. reader.readAsDataURL(blob);
  220. reader.onload = () => {
  221. link.href = reader.result;
  222. link.click();
  223. };
  224. }, 'image/png');
  225. }
  226. </script>
  227. @endsection