login.html 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. {{template "head" .}}
  4. <style>
  5. h1 {
  6. text-align: center;
  7. color: #fff;
  8. margin: 20px 0 50px 0;
  9. }
  10. .ant-btn, .ant-input {
  11. height: 50px;
  12. border-radius: 30px;
  13. }
  14. .ant-input-group-addon {
  15. border-radius: 0 30px 30px 0;
  16. width: 50px;
  17. font-size: 18px;
  18. }
  19. .ant-input-affix-wrapper .ant-input-prefix {
  20. left: 23px;
  21. }
  22. .ant-input-affix-wrapper .ant-input:not(:first-child) {
  23. padding-left: 50px;
  24. }
  25. .centered {
  26. display: flex;
  27. text-align: center;
  28. align-items: center;
  29. justify-content: center;
  30. }
  31. .title {
  32. font-size: 32px;
  33. font-weight: bold;
  34. }
  35. #app {
  36. overflow: hidden;
  37. }
  38. #login {
  39. animation: charge .5s both;
  40. background-color: #fff;
  41. border-radius: 2rem;
  42. padding: 3rem;
  43. }
  44. #login:hover {
  45. box-shadow: 0 2px 8px rgba(0,0,0,.09);
  46. }
  47. @keyframes charge {
  48. from {transform: translateY(5rem);opacity: 0}
  49. to {transform: translateY(0);opacity: 1}
  50. }
  51. @keyframes wave {
  52. from {transform: rotate(0deg);}
  53. to {transform: rotate(360deg);}
  54. }
  55. .wave {
  56. opacity: .6;
  57. position: absolute;
  58. bottom: 40%;
  59. left: 50%;
  60. width: 6000px;
  61. height: 6000px;
  62. background: #000;
  63. margin-left: -3000px;
  64. transform-origin: 50% 48%;
  65. border-radius: 46%;
  66. animation: wave 72s infinite linear;
  67. pointer-events: none;
  68. }
  69. .wave2 {
  70. animation: wave 88s infinite linear;
  71. opacity: .3;
  72. }
  73. .wave3 {
  74. animation: wave 80s infinite linear;
  75. opacity: .1;
  76. }
  77. .wave {
  78. background: #0a755715;
  79. }
  80. .under {
  81. background-color: #dbf5ed;
  82. }
  83. .dark .wave {
  84. background: rgb(10 117 87 / 20%);
  85. }
  86. .dark .under {
  87. background-color: #051510;
  88. }
  89. .dark #login {
  90. background-color: rgb(8, 22, 20);
  91. }
  92. .dark h1 {
  93. color: rgb(255 255 255 / 85%);
  94. }
  95. </style>
  96. <body>
  97. <a-layout id="app" v-cloak class="login" :class="themeSwitcher.currentTheme">
  98. <transition name="list" appear>
  99. <a-layout-content class="under">
  100. <div class='wave'></div>
  101. <div class='wave wave2'></div>
  102. <div class='wave wave3'></div>
  103. <a-row type="flex" justify="center" align="middle" style="height: 100%;">
  104. <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="6" id="login">
  105. <a-row type="flex" justify="center">
  106. <a-col>
  107. <h1 class="title">{{ i18n "pages.login.title" }}</h1>
  108. </a-col>
  109. </a-row>
  110. <a-row type="flex" justify="center">
  111. <a-col span="24">
  112. <a-form>
  113. <a-form-item>
  114. <a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
  115. @keydown.enter.native="login" autofocus>
  116. <a-icon slot="prefix" type="user" style="font-size: 16px;" />
  117. </a-input>
  118. </a-form-item>
  119. <a-form-item>
  120. <password-input icon="lock" v-model.trim="user.password"
  121. placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
  122. </password-input>
  123. </a-form-item>
  124. <a-form-item v-if="secretEnable">
  125. <password-input icon="key" v-model.trim="user.loginSecret"
  126. placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">
  127. </password-input>
  128. </a-input>
  129. </a-form-item>
  130. <a-form-item>
  131. <a-row justify="center" class="centered">
  132. <a-button type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
  133. :style="loading ? { width: '50px' } : { display: 'block', width: '100%' }">
  134. [[ loading ? '' : '{{ i18n "login" }}' ]]
  135. </a-button>
  136. </a-row>
  137. </a-form-item>
  138. <a-form-item>
  139. <a-row justify="center" class="centered">
  140. <a-col :span="24">
  141. <a-select ref="selectLang" v-model="lang" @change="setLang(lang)" :dropdown-class-name="themeSwitcher.currentTheme">
  142. <a-select-option :value="l.value" :label="l.value" v-for="l in supportLangs">
  143. <span role="img" :aria-label="l.name" v-text="l.icon"></span>
  144. &nbsp;&nbsp;<span v-text="l.name"></span>
  145. </a-select-option>
  146. </a-select>
  147. </a-col>
  148. </a-row>
  149. </a-form-item>
  150. <a-form-item>
  151. <a-row justify="center" class="centered">
  152. <a-col>
  153. <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>&nbsp;
  154. </a-col>
  155. <a-col>
  156. <theme-switch />
  157. </a-col>
  158. </a-row>
  159. </a-form-item>
  160. </a-form>
  161. </a-col>
  162. </a-row>
  163. </a-col>
  164. </a-row>
  165. </a-layout-content>
  166. </transition>
  167. </a-layout>
  168. {{template "js" .}}
  169. {{template "component/themeSwitcher" .}}
  170. {{template "component/password" .}}
  171. <script>
  172. const app = new Vue({
  173. delimiters: ['[[', ']]'],
  174. el: '#app',
  175. data: {
  176. themeSwitcher,
  177. loading: false,
  178. user: new User(),
  179. secretEnable: false,
  180. lang: ""
  181. },
  182. async created() {
  183. this.lang = getLang();
  184. this.secretEnable = await this.getSecretStatus();
  185. },
  186. methods: {
  187. async login() {
  188. this.loading = true;
  189. const msg = await HttpUtil.post('/login', this.user);
  190. this.loading = false;
  191. if (msg.success) {
  192. location.href = basePath + 'panel/';
  193. }
  194. },
  195. async getSecretStatus() {
  196. this.loading = true;
  197. const msg = await HttpUtil.post('/getSecretStatus');
  198. this.loading = false;
  199. if (msg.success) {
  200. this.secretEnable = msg.obj;
  201. return msg.obj;
  202. }
  203. },
  204. },
  205. });
  206. </script>
  207. </body>
  208. </html>