login.blade.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <style>
  2. .login-box {
  3. margin-top: -10rem;
  4. padding: 5px;
  5. }
  6. .login-card-body {
  7. padding: 1.5rem 1.8rem 1.6rem;
  8. }
  9. .card, .card-body {
  10. border-radius: .25rem
  11. }
  12. .login-btn {
  13. padding-left: 2rem!important;;
  14. padding-right: 1.5rem!important;
  15. width: 100%;
  16. }
  17. .content {
  18. overflow-x: hidden;
  19. }
  20. .form-group .control-label {
  21. text-align: left;
  22. }
  23. .justify-content-between {
  24. margin-top: 1rem;
  25. }
  26. #captcha {
  27. width: 185px;
  28. float: left;
  29. }
  30. .captcha-img {
  31. float: right;
  32. width: 110px;
  33. height: 34px;
  34. }
  35. </style>
  36. <div class="login-page bg-40">
  37. <div class="login-box">
  38. <div class="login-logo mb-2">
  39. {{ trans('admin.site_name') }}
  40. </div>
  41. <div class="card">
  42. <div class="card-body login-card-body shadow-100">
  43. {{-- <p class="login-box-msg mt-1 mb-1">{{ __('admin.welcome_back') }}</p>--}}
  44. <form id="login-form" method="POST" action="{{ admin_url('auth/login') }}">
  45. <input type="hidden" name="_token" value="{{ csrf_token() }}"/>
  46. <input type="hidden" name="timeZoneName" id="timeZoneName" value=""/>
  47. <fieldset class="form-label-group form-group position-relative has-icon-left">
  48. <input
  49. type="text"
  50. class="form-control {{ $errors->has('username') ? 'is-invalid' : '' }}"
  51. name="username"
  52. placeholder="{{ trans('admin.username') }}"
  53. value="{{ old('username') }}"
  54. required
  55. autofocus
  56. >
  57. <div class="form-control-position">
  58. <i class="feather icon-user"></i>
  59. </div>
  60. <label for="email">{{ trans('admin.username') }}</label>
  61. <div class="help-block with-errors"></div>
  62. @if($errors->has('username'))
  63. <span class="invalid-feedback text-danger" role="alert">
  64. @foreach($errors->get('username') as $message)
  65. <span class="control-label" for="inputError"><i class="feather icon-x-circle"></i> {{$message}}</span><br>
  66. @endforeach
  67. </span>
  68. @endif
  69. </fieldset>
  70. <fieldset class="form-label-group form-group position-relative has-icon-left">
  71. <input
  72. minlength="5"
  73. maxlength="20"
  74. id="password"
  75. type="password"
  76. class="form-control {{ $errors->has('password') ? 'is-invalid' : '' }}"
  77. name="password"
  78. placeholder="{{ trans('admin.password') }}"
  79. required
  80. autocomplete="current-password"
  81. >
  82. <div class="form-control-position">
  83. <i class="feather icon-lock"></i>
  84. </div>
  85. <label for="password">{{ trans('admin.password') }}</label>
  86. <div class="help-block with-errors"></div>
  87. @if($errors->has('password'))
  88. <span class="invalid-feedback text-danger" role="alert">
  89. @foreach($errors->get('password') as $message)
  90. <span class="control-label" for="inputError"><i class="feather icon-x-circle"></i> {{$message}}</span><br>
  91. @endforeach
  92. </span>
  93. @endif
  94. </fieldset>
  95. <fieldset class="form-label-group form-group position-relative has-icon-left">
  96. <input
  97. minlength="6"
  98. maxlength="6"
  99. id="captcha"
  100. type="captcha"
  101. class="form-control"
  102. name="captcha"
  103. placeholder="{{ trans('admin.captcha') }}"
  104. required
  105. >
  106. <img src="/dist/captcha?{{ time() }}" alt="captcha" class="captcha-img" onclick="this.src='/dist/captcha?'+Math.random()">
  107. <div class="form-control-position">
  108. <i class="feather icon-image"></i>
  109. </div>
  110. <label for="captcha">{{ trans('admin.captcha') }}</label>
  111. <div class="help-block with-errors"></div>
  112. @if($errors->has('captcha'))
  113. <span class="invalid-feedback text-danger" role="alert">
  114. @foreach($errors->get('captcha') as $message)
  115. <span class="control-label" for="inputError"><i class="feather icon-x-circle"></i> {{$message}}</span><br>
  116. @endforeach
  117. </span>
  118. @endif
  119. </fieldset>
  120. <button type="submit" class="btn btn-primary float-letf login-btn">
  121. {{ __('admin.login') }}
  122. <i class="feather icon-arrow-right"></i>
  123. </button>
  124. <div class="form-group d-flex justify-content-between align-items-center">
  125. <div class="text-left">
  126. @if(config('admin.auth.remember'))
  127. <fieldset class="checkbox">
  128. <div class="vs-checkbox-con vs-checkbox-primary">
  129. <input id="remember" name="remember" value="1" type="checkbox" {{ old('remember') ? 'checked' : '' }}>
  130. <span class="vs-checkbox">
  131. <span class="vs-checkbox--check">
  132. <i class="vs-icon feather icon-check"></i>
  133. </span>
  134. </span>
  135. <span> {{ trans('admin.remember_me') }}</span>
  136. </div>
  137. </fieldset>
  138. @endif
  139. </div>
  140. </div>
  141. <div class="form-group d-flex justify-content-between align-items-center">
  142. <div class="text-left">
  143. <a class="lang-item" href="javascript:void(0);" data-lang="en">English</a>
  144. / <a class="lang-item" href="javascript:void(0);" data-lang="zh_CN">中文</a>
  145. </div>
  146. </div>
  147. </form>
  148. </div>
  149. </div>
  150. </div>
  151. </div>
  152. <script>
  153. var getTimeZoneInfo = function () {
  154. const date = new Date();
  155. // 尝试获取时区名称(现代浏览器)
  156. let timeZoneName;
  157. try {
  158. timeZoneName = Intl.DateTimeFormat().resolvedOptions().timeZone;
  159. } catch (e) {
  160. timeZoneName = null;
  161. }
  162. // 计算偏移量(兼容所有浏览器)
  163. const offsetMinutes = date.getTimezoneOffset();
  164. const offsetHours = -offsetMinutes / 60;
  165. const offsetString = `UTC${offsetHours >= 0 ? '+' : ''}${offsetHours}`;
  166. return {
  167. name: timeZoneName || 'Unknown', // 时区名称(如 "Asia/Shanghai")
  168. offset: offsetString, // 偏移量(如 "UTC+8")
  169. offsetMinutes: offsetMinutes // 分钟差(如 -480)
  170. };
  171. }
  172. Dcat.ready(function () {
  173. $('#timeZoneName').val(getTimeZoneInfo().name);
  174. // ajax表单提交
  175. $('#login-form').form({
  176. validate: true,
  177. });
  178. });
  179. </script>
  180. <script>
  181. document.querySelectorAll('.lang-item').forEach(item => {
  182. item.addEventListener('click', function() {
  183. const lang = this.getAttribute('data-lang');
  184. switchLanguage(lang);
  185. });
  186. });
  187. function switchLanguage(lang) {
  188. fetch(`/dist/language-switch?lang=${encodeURIComponent(lang)}`, {
  189. method: 'GET'
  190. })
  191. .then(response => {
  192. if (!response.ok) {
  193. throw new Error('Network response was not ok');
  194. }
  195. return response.json();
  196. })
  197. .then(data => {
  198. if (data.success) {
  199. window.location.reload();
  200. } else {
  201. throw new Error(data.error || 'Language switch failed');
  202. }
  203. })
  204. .catch(error => {
  205. console.error('Language Switch Error:', error);
  206. alert('Failed to switch language. Please try again.');
  207. });
  208. }
  209. </script>