Login.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <script setup lang="ts">
  2. import { LockOutlined, UserOutlined } from '@ant-design/icons-vue'
  3. import { Form, message } from 'ant-design-vue'
  4. import { useUserStore } from '@/pinia'
  5. import auth from '@/api/auth'
  6. import install from '@/api/install'
  7. import SetLanguage from '@/components/SetLanguage/SetLanguage.vue'
  8. import SwitchAppearance from '@/components/SwitchAppearance/SwitchAppearance.vue'
  9. import gettext from '@/gettext'
  10. const thisYear = new Date().getFullYear()
  11. const route = useRoute()
  12. const router = useRouter()
  13. install.get_lock().then(async (r: { lock: boolean }) => {
  14. if (!r.lock)
  15. await router.push('/install')
  16. })
  17. const loading = ref(false)
  18. const modelRef = reactive({
  19. username: '',
  20. password: '',
  21. })
  22. const rulesRef = reactive({
  23. username: [
  24. {
  25. required: true,
  26. message: () => $gettext('Please input your username!'),
  27. },
  28. ],
  29. password: [
  30. {
  31. required: true,
  32. message: () => $gettext('Please input your password!'),
  33. },
  34. ],
  35. })
  36. const { validate, validateInfos, clearValidate } = Form.useForm(modelRef, rulesRef)
  37. const onSubmit = () => {
  38. validate().then(async () => {
  39. loading.value = true
  40. await auth.login(modelRef.username, modelRef.password).then(async () => {
  41. message.success($gettext('Login successful'), 1)
  42. const next = (route.query?.next || '').toString() || '/'
  43. await router.push(next)
  44. }).catch(e => {
  45. switch (e.code) {
  46. case 4031:
  47. message.error($gettext('Incorrect username or password'))
  48. break
  49. case 4291:
  50. message.error($gettext('Too many login failed attempts, please try again later'))
  51. break
  52. case 4033:
  53. message.error($gettext('User is banned'))
  54. break
  55. default:
  56. message.error($gettext(e.message ?? 'Server error'))
  57. break
  58. }
  59. })
  60. loading.value = false
  61. })
  62. }
  63. const user = useUserStore()
  64. if (user.is_login) {
  65. const next = (route.query?.next || '').toString() || '/dashboard'
  66. router.push(next)
  67. }
  68. watch(() => gettext.current, () => {
  69. clearValidate()
  70. })
  71. const has_casdoor = ref(false)
  72. const casdoor_uri = ref('')
  73. auth.get_casdoor_uri()
  74. .then(r => {
  75. if (r?.uri) {
  76. has_casdoor.value = true
  77. casdoor_uri.value = r.uri
  78. }
  79. })
  80. .catch(e => {
  81. message.error($gettext(e.message ?? 'Server error'))
  82. })
  83. const loginWithCasdoor = () => {
  84. window.location.href = casdoor_uri.value
  85. }
  86. if (route.query?.code !== undefined && route.query?.state !== undefined) {
  87. loading.value = true
  88. auth.casdoor_login(route.query?.code?.toString(), route.query?.state?.toString()).then(async () => {
  89. message.success($gettext('Login successful'), 1)
  90. const next = (route.query?.next || '').toString() || '/'
  91. await router.push(next)
  92. }).catch(e => {
  93. message.error($gettext(e.message ?? 'Server error'))
  94. })
  95. loading.value = false
  96. }
  97. </script>
  98. <template>
  99. <ALayout>
  100. <ALayoutContent>
  101. <div class="login-container">
  102. <div class="login-form">
  103. <div class="project-title">
  104. <h1>Nginx UI</h1>
  105. </div>
  106. <AForm id="components-form-demo-normal-login">
  107. <AFormItem v-bind="validateInfos.username">
  108. <AInput
  109. v-model:value="modelRef.username"
  110. :placeholder="$gettext('Username')"
  111. >
  112. <template #prefix>
  113. <UserOutlined style="color: rgba(0, 0, 0, 0.25)" />
  114. </template>
  115. </AInput>
  116. </AFormItem>
  117. <AFormItem v-bind="validateInfos.password">
  118. <AInputPassword
  119. v-model:value="modelRef.password"
  120. :placeholder="$gettext('Password')"
  121. >
  122. <template #prefix>
  123. <LockOutlined style="color: rgba(0, 0, 0, 0.25)" />
  124. </template>
  125. </AInputPassword>
  126. </AFormItem>
  127. <AFormItem>
  128. <AButton
  129. type="primary"
  130. block
  131. html-type="submit"
  132. :loading="loading"
  133. @click="onSubmit"
  134. >
  135. {{ $gettext('Login') }}
  136. </AButton>
  137. </AFormItem>
  138. </AForm>
  139. <AButton
  140. v-if="has_casdoor"
  141. block
  142. html-type="submit"
  143. :loading="loading"
  144. @click="loginWithCasdoor"
  145. >
  146. {{ $gettext('SSO Login') }}
  147. </AButton>
  148. <div class="footer">
  149. <p>Copyright © 2021 - {{ thisYear }} Nginx UI</p>
  150. Language
  151. <SetLanguage class="inline" />
  152. <div class="flex justify-center mt-4">
  153. <SwitchAppearance />
  154. </div>
  155. </div>
  156. </div>
  157. </div>
  158. </ALayoutContent>
  159. </ALayout>
  160. </template>
  161. <style lang="less" scoped>
  162. .ant-layout-content {
  163. background: #fff;
  164. }
  165. .dark .ant-layout-content {
  166. background: transparent;
  167. }
  168. .login-container {
  169. display: flex;
  170. align-items: center;
  171. justify-content: center;
  172. height: 100vh;
  173. .login-form {
  174. max-width: 400px;
  175. width: 80%;
  176. .project-title {
  177. margin: 50px;
  178. h1 {
  179. font-size: 50px;
  180. font-weight: 100;
  181. text-align: center;
  182. }
  183. }
  184. .anticon {
  185. color: #a8a5a5 !important;
  186. }
  187. .footer {
  188. padding: 30px;
  189. text-align: center;
  190. font-size: 14px;
  191. }
  192. }
  193. }
  194. </style>