LoginView.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <script setup>
  2. import { ref } from 'vue'
  3. import { useRouter } from 'vue-router'
  4. import { Form } from '@primevue/forms'
  5. import Card from 'primevue/card'
  6. import Button from 'primevue/button'
  7. import InputText from 'primevue/inputtext'
  8. import Password from 'primevue/password'
  9. import Message from 'primevue/message'
  10. import FloatLabel from 'primevue/floatlabel'
  11. import IconField from 'primevue/iconfield'
  12. import InputIcon from 'primevue/inputicon'
  13. import { useToast } from 'primevue/usetoast'
  14. import { useUserStore } from '@/stores/user'
  15. import { zodResolver } from '@primevue/forms/resolvers/zod'
  16. import { z } from 'zod'
  17. const toast = useToast()
  18. const router = useRouter()
  19. const loading = ref(false)
  20. const { login } = useUserStore()
  21. const loginForm = ref({
  22. name: '',
  23. password: ''
  24. })
  25. const resolver = zodResolver(
  26. z.object({
  27. name: z.string().min(1, { message: '用户名不能为空' }),
  28. password: z.string().min(8, { message: '密码至少8位' })
  29. })
  30. )
  31. const onFormSubmit = async ({ valid, values }) => {
  32. if (!valid) {
  33. return
  34. }
  35. loading.value = true
  36. try {
  37. await login(values.name, values.password)
  38. toast.add({ severity: 'success', summary: '成功', detail: '登录成功', life: 3000 })
  39. router.push('/')
  40. } catch (error) {
  41. toast.add({
  42. severity: 'error',
  43. summary: '错误',
  44. detail: error.message || '登录失败,请重试',
  45. life: 3000
  46. })
  47. } finally {
  48. loading.value = false
  49. }
  50. }
  51. </script>
  52. <template>
  53. <div class="login-page p-d-flex p-jc-center p-ai-center">
  54. <Card class="login-card">
  55. <template #title>
  56. <div class="text-center">
  57. <h2>系统登录</h2>
  58. </div>
  59. </template>
  60. <template #content>
  61. <Form v-slot="$form" :resolver="resolver" :initialValues="loginForm" @submit="onFormSubmit" class="p-fluid">
  62. <div class="field mt-8">
  63. <FloatLabel variant="on">
  64. <IconField>
  65. <InputIcon class="pi pi-user" />
  66. <InputText id="name" name="name" v-model="loginForm.name" autocomplete="off" fluid />
  67. </IconField>
  68. <label for="name">用户名</label>
  69. </FloatLabel>
  70. <Message v-if="$form.name?.invalid" severity="error" size="small" variant="simple">{{
  71. $form.name.error?.message
  72. }}</Message>
  73. </div>
  74. <div class="field mt-8">
  75. <FloatLabel variant="on">
  76. <IconField>
  77. <InputIcon class="pi pi-lock" />
  78. <Password
  79. id="password"
  80. name="password"
  81. v-model="loginForm.password"
  82. toggleMask
  83. :feedback="false"
  84. fluid
  85. />
  86. </IconField>
  87. <label for="password">密码</label>
  88. </FloatLabel>
  89. <Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
  90. $form.password.error?.message
  91. }}</Message>
  92. </div>
  93. <div class="field mt-8">
  94. <Button label="登录" type="submit" fluid />
  95. </div>
  96. </Form>
  97. </template>
  98. </Card>
  99. </div>
  100. </template>
  101. <style scoped>
  102. .login-page {
  103. background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
  104. background-size: 400% 400%;
  105. animation: gradient 15s ease infinite;
  106. height: 100vh;
  107. display: flex;
  108. justify-content: center;
  109. align-items: center;
  110. }
  111. @keyframes gradient {
  112. 0% {
  113. background-position: 0% 50%;
  114. }
  115. 50% {
  116. background-position: 100% 50%;
  117. }
  118. 100% {
  119. background-position: 0% 50%;
  120. }
  121. }
  122. .login-card {
  123. width: 30rem;
  124. max-width: 90%;
  125. border-radius: 1rem;
  126. box-shadow:
  127. 0 2px 1px -1px rgba(0, 0, 0, 0.2),
  128. 0 1px 1px 0 rgba(0, 0, 0, 0.14),
  129. 0 1px 3px 0 rgba(0, 0, 0, 0.12);
  130. background: rgba(255, 255, 255, 0.9);
  131. backdrop-filter: blur(10px);
  132. border: 1px solid rgba(255, 255, 255, 0.2);
  133. }
  134. .text-center {
  135. text-align: center;
  136. }
  137. .block {
  138. display: block;
  139. }
  140. .w-full {
  141. width: 100%;
  142. }
  143. </style>