LoginForm.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <template>
  2. <LoginFormTitle v-show="getShow" class="enter-x" />
  3. <Form
  4. class="p-4 enter-x"
  5. :model="formData"
  6. :rules="getFormRules"
  7. ref="formRef"
  8. v-show="getShow"
  9. @keypress.enter="handleLogin"
  10. >
  11. <FormItem name="account" class="enter-x">
  12. <Input
  13. size="large"
  14. v-model:value="formData.account"
  15. :placeholder="t('sys.login.userName')"
  16. class="fix-auto-fill"
  17. />
  18. </FormItem>
  19. <FormItem name="password" class="enter-x">
  20. <InputPassword
  21. size="large"
  22. visibilityToggle
  23. v-model:value="formData.password"
  24. :placeholder="t('sys.login.password')"
  25. />
  26. </FormItem>
  27. <!-- 文本验证码 -->
  28. <ARow v-if="computedUseCaptcha == 'TEXT'" :gutter="16">
  29. <ACol :span="16">
  30. <FormItem name="captcha">
  31. <Input
  32. v-model:value="formData.captcha"
  33. class="login-captcha"
  34. :placeholder="t('system.login.login-captcha')"
  35. size="large"
  36. />
  37. </FormItem>
  38. </ACol>
  39. <ACol :span="8">
  40. <TextCaptcha
  41. ref="captchaRef"
  42. @after-refresh="({ key }) => (formData.key = key)"
  43. height="40px"
  44. :api="getCaptchaApi"
  45. />
  46. </ACol>
  47. </ARow>
  48. <ARow class="enter-x">
  49. <ACol :span="12">
  50. <FormItem>
  51. <!-- No logic, you need to deal with it yourself -->
  52. <Checkbox v-model:checked="rememberMe" size="small">
  53. {{ t('sys.login.rememberMe') }}
  54. </Checkbox>
  55. </FormItem>
  56. </ACol>
  57. <ACol :span="12">
  58. <FormItem :style="{ 'text-align': 'right' }">
  59. <!-- No logic, you need to deal with it yourself -->
  60. <Button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">
  61. {{ t('sys.login.forgetPassword') }}
  62. </Button>
  63. </FormItem>
  64. </ACol>
  65. </ARow>
  66. <FormItem class="enter-x">
  67. <Button type="primary" size="large" block @click="handleLogin" :loading="loading">
  68. {{ t('sys.login.loginButton') }}
  69. </Button>
  70. <!-- <Button size="large" class="mt-4 enter-x" block @click="handleRegister">
  71. {{ t('sys.login.registerButton') }}
  72. </Button> -->
  73. </FormItem>
  74. <ARow class="enter-x" :gutter="[16, 16]">
  75. <ACol :md="8" :xs="24">
  76. <Button block @click="setLoginState(LoginStateEnum.MOBILE)">
  77. {{ t('sys.login.mobileSignInFormTitle') }}
  78. </Button>
  79. </ACol>
  80. <ACol :md="8" :xs="24">
  81. <Button block @click="setLoginState(LoginStateEnum.QR_CODE)">
  82. {{ t('sys.login.qrSignInFormTitle') }}
  83. </Button>
  84. </ACol>
  85. <ACol :md="8" :xs="24">
  86. <Button block @click="setLoginState(LoginStateEnum.REGISTER)">
  87. {{ t('sys.login.registerButton') }}
  88. </Button>
  89. </ACol>
  90. </ARow>
  91. <Divider class="enter-x">{{ t('sys.login.otherSignIn') }}</Divider>
  92. <div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
  93. <GithubFilled />
  94. <WechatFilled />
  95. <AlipayCircleFilled />
  96. <GoogleCircleFilled />
  97. <TwitterCircleFilled />
  98. </div>
  99. </Form>
  100. </template>
  101. <script lang="ts" setup>
  102. import { computed, reactive, ref, unref } from 'vue';
  103. import { Button, Checkbox, Col, Divider, Form, Input, Row } from 'ant-design-vue';
  104. import {
  105. AlipayCircleFilled,
  106. GithubFilled,
  107. GoogleCircleFilled,
  108. TwitterCircleFilled,
  109. WechatFilled,
  110. } from '@ant-design/icons-vue';
  111. import LoginFormTitle from './LoginFormTitle.vue';
  112. import { useI18n } from '@/hooks/web/useI18n';
  113. import { useMessage } from '@/hooks/web/useMessage';
  114. import { useUserStore } from '@/store/modules/user';
  115. import { LoginStateEnum, useFormRules, useFormValid, useLoginState } from './useLogin';
  116. import { useDesign } from '@/hooks/web/useDesign';
  117. import { ApiServiceEnum, defHttp } from '@/utils/http/axios';
  118. import { createPassword } from '@/utils/auth';
  119. import { useAppStore } from '@/store/modules/app';
  120. import { TextCaptcha } from '@/components/Verify';
  121. //import { onKeyStroke } from '@vueuse/core';
  122. const captchaRef = ref();
  123. const ACol = Col;
  124. const ARow = Row;
  125. const FormItem = Form.Item;
  126. const InputPassword = Input.Password;
  127. const { t } = useI18n();
  128. const { notification, createErrorModal } = useMessage();
  129. const { prefixCls } = useDesign('login');
  130. const userStore = useUserStore();
  131. const appStore = useAppStore();
  132. const { setLoginState, getLoginState } = useLoginState();
  133. const { getFormRules } = useFormRules();
  134. const formRef = ref();
  135. const loading = ref(false);
  136. const rememberMe = ref(false);
  137. /**
  138. * 是否使用验证码
  139. */
  140. const computedUseCaptcha = computed(() => {
  141. return appStore.systemProperties.captchaIdent;
  142. });
  143. const formData = reactive({
  144. account: 'admin',
  145. password: '123456',
  146. captcha: '',
  147. key: '',
  148. });
  149. const { validForm } = useFormValid(formRef);
  150. //onKeyStroke('Enter', handleLogin);
  151. const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
  152. async function handleLogin() {
  153. const data = await validForm();
  154. if (!data) return;
  155. try {
  156. loading.value = true;
  157. const userInfo = await userStore.login({
  158. password: createPassword(data.account, data.password),
  159. username: data.account,
  160. mode: 'none', //不要默认的错误提示
  161. key: formData.key,
  162. code: JSON.stringify(unref(captchaRef).createValidateParameter(formData.captcha)),
  163. });
  164. if (userInfo) {
  165. notification.success({
  166. message: t('sys.login.loginSuccessTitle'),
  167. description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
  168. duration: 3,
  169. });
  170. }
  171. } catch (error) {
  172. createErrorModal({
  173. title: t('sys.api.errorTip'),
  174. content: (error as unknown as Error).message || t('sys.api.networkExceptionMsg'),
  175. getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body,
  176. });
  177. unref(captchaRef).refresh();
  178. } finally {
  179. loading.value = false;
  180. }
  181. }
  182. const getCaptchaApi = () => {
  183. return defHttp.post({
  184. service: ApiServiceEnum.SMART_AUTH,
  185. url: 'auth/createCaptcha',
  186. });
  187. };
  188. </script>