login-form.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <template>
  2. <div class="login-form-wrapper">
  3. <div class="login-form-title">{{ t('login.form.title') }}</div>
  4. <div class="login-form-sub-title">{{ t('login.form.title') }}</div>
  5. <div class="login-form-error-msg">{{ errorMessage }}</div>
  6. <a-form
  7. ref="loginForm"
  8. :model="userInfo"
  9. class="login-form"
  10. layout="vertical"
  11. @submit="handleSubmit"
  12. >
  13. <a-form-item
  14. field="username"
  15. :rules="[{ required: true, message: t('login.form.userName.errMsg') }]"
  16. :validate-trigger="['change', 'blur']"
  17. hide-label
  18. >
  19. <a-input
  20. v-model="userInfo.username"
  21. :placeholder="t('login.form.userName.placeholder')"
  22. >
  23. <template #prefix>
  24. <icon-user />
  25. </template>
  26. </a-input>
  27. </a-form-item>
  28. <a-form-item
  29. field="password"
  30. :rules="[{ required: true, message: t('login.form.password.errMsg') }]"
  31. :validate-trigger="['change', 'blur']"
  32. hide-label
  33. >
  34. <a-input-password
  35. v-model="userInfo.password"
  36. :placeholder="t('login.form.password.placeholder')"
  37. allow-clear
  38. >
  39. <template #prefix>
  40. <icon-lock />
  41. </template>
  42. </a-input-password>
  43. </a-form-item>
  44. <a-space :size="16" direction="vertical">
  45. <div class="login-form-password-actions">
  46. <a-checkbox
  47. checked="rememberPassword"
  48. :model-value="loginConfig.rememberPassword"
  49. @change="setRememberPassword as any"
  50. >
  51. {{ t('login.form.rememberPassword') }}
  52. </a-checkbox>
  53. <a-link>{{ t('login.form.forgetPassword') }}</a-link>
  54. </div>
  55. <a-button type="primary" html-type="submit" long :loading="loading">
  56. {{ t('login.form.login') }}
  57. </a-button>
  58. <a-button type="text" long class="login-form-register-btn">
  59. {{ t('login.form.register') }}
  60. </a-button>
  61. </a-space>
  62. </a-form>
  63. </div>
  64. </template>
  65. <script lang="ts" setup>
  66. import { ref, reactive } from 'vue';
  67. import { useRouter } from 'vue-router';
  68. import { Message } from '@arco-design/web-vue';
  69. import { ValidatedError } from '@arco-design/web-vue/es/form/interface';
  70. import { useI18n } from 'vue-i18n';
  71. import { useStorage } from '@vueuse/core';
  72. import { useUserStore } from '@/store';
  73. import useLoading from '@/hooks/loading';
  74. import type { LoginData } from '@/api/user';
  75. const router = useRouter();
  76. const { t } = useI18n();
  77. const errorMessage = ref('');
  78. const { loading, setLoading } = useLoading();
  79. const userStore = useUserStore();
  80. const loginConfig = useStorage('smms-login', {
  81. rememberPassword: true,
  82. username: 'admin', // 演示默认值
  83. });
  84. const userInfo = reactive({
  85. username: loginConfig.value.username,
  86. password: null,
  87. });
  88. const handleSubmit = async ({
  89. errors,
  90. values,
  91. }: {
  92. errors: Record<string, ValidatedError> | undefined;
  93. values: Record<string, any>;
  94. }) => {
  95. if (loading.value) return;
  96. if (!errors) {
  97. setLoading(true);
  98. try {
  99. const res = await userStore.login(values as LoginData);
  100. const { redirect, ...othersQuery } = router.currentRoute.value.query;
  101. if (res.success) {
  102. await router.push({
  103. name: (redirect as string) || 'Workplace',
  104. query: {
  105. ...othersQuery,
  106. },
  107. });
  108. Message.success(t('login.form.login.success'));
  109. const { rememberPassword } = loginConfig.value;
  110. const { username, password } = values;
  111. // 实际生产环境需要进行加密存储。
  112. // The actual production environment requires encrypted storage.
  113. loginConfig.value.username = rememberPassword ? username : '';
  114. } else {
  115. Message.error(res.message);
  116. }
  117. } catch (err) {
  118. errorMessage.value = (err as Error).message;
  119. } finally {
  120. setLoading(false);
  121. }
  122. }
  123. };
  124. const setRememberPassword = (value: boolean) => {
  125. loginConfig.value.rememberPassword = value;
  126. };
  127. </script>
  128. <style lang="less" scoped>
  129. .login-form {
  130. &-wrapper {
  131. width: 320px;
  132. }
  133. &-title {
  134. color: var(--color-text-1);
  135. font-weight: 500;
  136. font-size: 24px;
  137. line-height: 32px;
  138. }
  139. &-sub-title {
  140. color: var(--color-text-3);
  141. font-size: 16px;
  142. line-height: 24px;
  143. }
  144. &-error-msg {
  145. height: 32px;
  146. color: rgb(var(--red-6));
  147. line-height: 32px;
  148. }
  149. &-password-actions {
  150. display: flex;
  151. justify-content: space-between;
  152. }
  153. &-register-btn {
  154. color: var(--color-text-3) !important;
  155. }
  156. }
  157. </style>