login.html 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. {{template "head" .}}
  4. <style>
  5. h1 {
  6. text-align: center;
  7. margin: 20px 0 50px 0;
  8. }
  9. .ant-btn,
  10. .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 0.5s both;
  40. background-color: #fff;
  41. border-radius: 2rem;
  42. padding: 3rem;
  43. transition: all 0.3s;
  44. }
  45. #login:hover {
  46. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
  47. }
  48. @keyframes charge {
  49. from {
  50. transform: translateY(5rem);
  51. opacity: 0;
  52. }
  53. to {
  54. transform: translateY(0);
  55. opacity: 1;
  56. }
  57. }
  58. .wave {
  59. opacity: 0.6;
  60. position: absolute;
  61. bottom: 40%;
  62. left: 50%;
  63. width: 6000px;
  64. height: 6000px;
  65. background-color: rgba(0, 135, 113, 0.08);
  66. margin-left: -3000px;
  67. transform-origin: 50% 48%;
  68. border-radius: 46%;
  69. pointer-events: none;
  70. rotate: 125deg;
  71. }
  72. .wave2 {
  73. opacity: 0.4;
  74. rotate: 70deg;
  75. }
  76. .wave3 {
  77. opacity: 0.2;
  78. rotate: 90deg;
  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: #101828;
  88. }
  89. .dark #login {
  90. background-color: #151f31;
  91. }
  92. .dark h1 {
  93. color: rgba(255, 255, 255, 0.85);
  94. }
  95. .ant-btn-primary-login {
  96. color: #008771;
  97. background-color: #e8f4f2;
  98. border-color: #a2d3cb;
  99. text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
  100. box-shadow: none;
  101. width: 100%;
  102. }
  103. .ant-btn-primary-login:focus,
  104. .ant-btn-primary-login:hover {
  105. color: #fff;
  106. background-color: #006655;
  107. border-color: #006655;
  108. background-image: linear-gradient(
  109. 270deg,
  110. rgba(123, 199, 77, 0) 30%,
  111. #009980,
  112. rgba(123, 199, 77, 0) 100%
  113. );
  114. background-repeat: no-repeat;
  115. animation: ma-bg-move ease-in-out 5s infinite;
  116. background-position-x: -500px;
  117. width: 95%;
  118. animation-delay: -0.5s;
  119. box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
  120. }
  121. .ant-btn-primary-login.active,
  122. .ant-btn-primary-login:active {
  123. color: #fff;
  124. background-color: #006655;
  125. border-color: #006655;
  126. }
  127. @keyframes ma-bg-move {
  128. 0% {
  129. background-position: -500px 0;
  130. }
  131. 50% {
  132. background-position: 1000px 0;
  133. }
  134. 100% {
  135. background-position: 1000px 0;
  136. }
  137. }
  138. .wave-btn-bg {
  139. position: relative;
  140. border-radius: 25px;
  141. width: 100%;
  142. }
  143. .dark .wave-btn-bg {
  144. color: #fff;
  145. position: relative;
  146. background-color: #0a7557;
  147. border: 2px double transparent;
  148. background-origin: border-box;
  149. background-clip: padding-box, border-box;
  150. background-size: 300%;
  151. transition: all 0.5s ease;
  152. width: 100%;
  153. }
  154. .dark .wave-btn-bg:hover {animation: wave-btn-tara 4s ease infinite;}
  155. .dark .wave-btn-bg-cl {
  156. background-image: linear-gradient(rgba(13, 14, 33, 0), rgba(13, 14, 33, 0)),
  157. radial-gradient(circle at left top, #006655, #009980, #006655) !important;
  158. border-radius: 3em;
  159. }
  160. .dark .wave-btn-bg-cl:hover {
  161. width: 95%;
  162. }
  163. .dark .wave-btn-bg-cl:before {
  164. position: absolute;
  165. content: "";
  166. top: -5px;
  167. left: -5px;
  168. bottom: -5px;
  169. right: -5px;
  170. z-index: -1;
  171. background: inherit;
  172. background-size: inherit;
  173. border-radius: 4em;
  174. opacity: 0;
  175. transition: 0.5s;
  176. }
  177. .dark .wave-btn-bg-cl:hover::before {
  178. opacity: 1;
  179. filter: blur(20px);
  180. animation: wave-btn-tara 8s linear infinite;
  181. }
  182. @keyframes wave-btn-tara {
  183. to {
  184. background-position: 300%;
  185. }
  186. }
  187. .dark .ant-btn-primary-login {
  188. font-size: 14px;
  189. color: #fff;
  190. text-align: center;
  191. background-image: linear-gradient(
  192. rgba(13, 14, 33, 0.45),
  193. rgba(13, 14, 33, 0.35)
  194. );
  195. border-radius: 2rem;
  196. border: none;
  197. outline: none;
  198. background-color: transparent;
  199. height: 46px;
  200. position: relative;
  201. white-space: nowrap;
  202. cursor: pointer;
  203. touch-action: manipulation;
  204. padding: 0 15px;
  205. width: 100%;
  206. animation: none;
  207. background-position-x: 0;
  208. box-shadow: none;
  209. }
  210. </style>
  211. <body>
  212. <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
  213. <transition name="list" appear>
  214. <a-layout-content class="under" style="min-height: 0;">
  215. <div class='wave'></div>
  216. <div class='wave wave2'></div>
  217. <div class='wave wave3'></div>
  218. <a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;">
  219. <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
  220. <a-row type="flex" justify="center">
  221. <a-col>
  222. <h1 class="title">{{ i18n "pages.login.title" }}</h1>
  223. </a-col>
  224. </a-row>
  225. <a-row type="flex" justify="center">
  226. <a-col span="24">
  227. <a-form>
  228. <a-form-item>
  229. <a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
  230. @keydown.enter.native="login" autofocus>
  231. <a-icon slot="prefix" type="user" style="font-size: 16px;"/>
  232. </a-input>
  233. </a-form-item>
  234. <a-form-item>
  235. <password-input icon="lock" v-model.trim="user.password"
  236. placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
  237. </password-input>
  238. </a-form-item>
  239. <a-form-item v-if="secretEnable">
  240. <password-input icon="key" v-model.trim="user.loginSecret"
  241. placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">
  242. </password-input>
  243. </a-input>
  244. </a-form-item>
  245. <a-form-item>
  246. <a-row justify="center" class="centered">
  247. <div class="wave-btn-bg wave-btn-bg-cl">
  248. <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
  249. :style="loading ? { width: '50px' } : { display: 'inline-block' }">
  250. [[ loading ? '' : '{{ i18n "login" }}' ]]
  251. </a-button>
  252. </div>
  253. </a-row>
  254. </a-form-item>
  255. <a-form-item>
  256. <a-row justify="center" class="centered">
  257. <a-col :span="24">
  258. <a-select ref="selectLang" v-model="lang" @change="setLang(lang)" style="width: 150px;" :dropdown-class-name="themeSwitcher.currentTheme">
  259. <a-select-option :value="l.value" label="English" v-for="l in supportLangs">
  260. <span role="img" aria-label="l.name" v-text="l.icon"></span>
  261. &nbsp;&nbsp;<span v-text="l.name"></span>
  262. </a-select-option>
  263. </a-select>
  264. </a-col>
  265. </a-row>
  266. </a-form-item>
  267. <a-form-item>
  268. <a-row justify="center" class="centered">
  269. <a-col>
  270. <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>&nbsp;
  271. </a-col>
  272. <a-col>
  273. <theme-switch />
  274. </a-col>
  275. </a-row>
  276. </a-form-item>
  277. </a-form>
  278. </a-col>
  279. </a-row>
  280. </a-col>
  281. </a-row>
  282. </a-layout-content>
  283. </transition>
  284. </a-layout>
  285. {{template "js" .}}
  286. {{template "component/themeSwitcher" .}}
  287. {{template "component/password" .}}
  288. <script>
  289. class User {
  290. constructor() {
  291. this.username = "";
  292. this.password = "";
  293. }
  294. }
  295. const app = new Vue({
  296. delimiters: ['[[', ']]'],
  297. el: '#app',
  298. data: {
  299. themeSwitcher,
  300. loading: false,
  301. user: new User(),
  302. secretEnable: false,
  303. lang: ""
  304. },
  305. async created() {
  306. this.lang = getLang();
  307. this.secretEnable = await this.getSecretStatus();
  308. },
  309. methods: {
  310. async login() {
  311. this.loading = true;
  312. const msg = await HttpUtil.post('/login', this.user);
  313. this.loading = false;
  314. if (msg.success) {
  315. location.href = basePath + 'panel/';
  316. }
  317. },
  318. async getSecretStatus() {
  319. this.loading = true;
  320. const msg = await HttpUtil.post('/getSecretStatus');
  321. this.loading = false;
  322. if (msg.success) {
  323. this.secretEnable = msg.obj;
  324. return msg.obj;
  325. }
  326. },
  327. },
  328. });
  329. </script>
  330. </body>
  331. </html>