Login.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <div class="login-page">
  3. <div class="login-container">
  4. <!-- Logo 区域 -->
  5. <div class="login-header">
  6. <div class="logo">🚀 灵越智报</div>
  7. <p class="subtitle">智能报告生成平台</p>
  8. </div>
  9. <!-- 登录表单 -->
  10. <el-card class="login-card">
  11. <template #header>
  12. <div class="card-header">
  13. <span>用户登录</span>
  14. <router-link to="/register" class="switch-link">没有账号?立即注册</router-link>
  15. </div>
  16. </template>
  17. <el-form
  18. ref="formRef"
  19. :model="form"
  20. :rules="rules"
  21. label-width="0"
  22. size="large"
  23. @submit.prevent="handleLogin"
  24. >
  25. <el-form-item prop="usernameOrEmail">
  26. <el-input
  27. v-model="form.usernameOrEmail"
  28. placeholder="用户名或邮箱"
  29. :prefix-icon="User"
  30. clearable
  31. />
  32. </el-form-item>
  33. <el-form-item prop="password">
  34. <el-input
  35. v-model="form.password"
  36. type="password"
  37. placeholder="密码"
  38. :prefix-icon="Lock"
  39. show-password
  40. @keyup.enter="handleLogin"
  41. />
  42. </el-form-item>
  43. <el-form-item>
  44. <div class="login-options">
  45. <el-checkbox v-model="rememberMe">记住我</el-checkbox>
  46. <a href="#" class="forgot-password">忘记密码?</a>
  47. </div>
  48. </el-form-item>
  49. <el-form-item>
  50. <el-button
  51. type="primary"
  52. class="login-btn"
  53. :loading="loading"
  54. @click="handleLogin"
  55. >
  56. {{ loading ? '登录中...' : '登 录' }}
  57. </el-button>
  58. </el-form-item>
  59. </el-form>
  60. <!-- 其他登录方式 -->
  61. <div class="other-login">
  62. <el-divider>其他登录方式</el-divider>
  63. <div class="social-login">
  64. <el-button :icon="ChatDotRound" circle title="微信登录" />
  65. <el-button :icon="Message" circle title="企业微信" />
  66. <el-button :icon="Phone" circle title="手机验证码" />
  67. </div>
  68. </div>
  69. </el-card>
  70. <!-- 底部信息 -->
  71. <div class="login-footer">
  72. <p>© 2026 灵越智报 - 企业级智能报告生成平台</p>
  73. </div>
  74. </div>
  75. </div>
  76. </template>
  77. <script setup>
  78. import { ref, reactive } from 'vue'
  79. import { useRouter } from 'vue-router'
  80. import { User, Lock, ChatDotRound, Message, Phone } from '@element-plus/icons-vue'
  81. import { ElMessage } from 'element-plus'
  82. import { authApi } from '@/api'
  83. const router = useRouter()
  84. const formRef = ref(null)
  85. const loading = ref(false)
  86. const rememberMe = ref(false)
  87. const form = reactive({
  88. usernameOrEmail: '',
  89. password: ''
  90. })
  91. const rules = {
  92. usernameOrEmail: [
  93. { required: true, message: '请输入用户名或邮箱', trigger: 'blur' }
  94. ],
  95. password: [
  96. { required: true, message: '请输入密码', trigger: 'blur' }
  97. ]
  98. }
  99. async function handleLogin() {
  100. if (!formRef.value) return
  101. await formRef.value.validate(async (valid) => {
  102. if (!valid) return
  103. loading.value = true
  104. try {
  105. const response = await authApi.login(form)
  106. // 保存 Token(新后端返回结构: { accessToken, refreshToken, expiresIn, user: { id, username, realName, roles } })
  107. localStorage.setItem('accessToken', response.accessToken)
  108. localStorage.setItem('refreshToken', response.refreshToken)
  109. localStorage.setItem('userId', response.user?.id)
  110. localStorage.setItem('username', response.user?.realName || response.user?.username)
  111. if (rememberMe.value) {
  112. localStorage.setItem('rememberMe', 'true')
  113. }
  114. ElMessage.success('登录成功')
  115. router.push('/')
  116. } catch (error) {
  117. console.error('登录失败:', error)
  118. ElMessage.error(error.message || '登录失败,请检查用户名和密码')
  119. } finally {
  120. loading.value = false
  121. }
  122. })
  123. }
  124. </script>
  125. <style lang="scss" scoped>
  126. .login-page {
  127. min-height: 100vh;
  128. display: flex;
  129. align-items: center;
  130. justify-content: center;
  131. background: linear-gradient(135deg, #1890ff 0%, #096dd9 50%, #722ed1 100%);
  132. padding: 20px;
  133. }
  134. .login-container {
  135. width: 100%;
  136. max-width: 420px;
  137. }
  138. .login-header {
  139. text-align: center;
  140. margin-bottom: 30px;
  141. color: #fff;
  142. .logo {
  143. font-size: 32px;
  144. font-weight: 700;
  145. margin-bottom: 8px;
  146. }
  147. .subtitle {
  148. font-size: 14px;
  149. opacity: 0.9;
  150. }
  151. }
  152. .login-card {
  153. border-radius: 12px;
  154. box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  155. .card-header {
  156. display: flex;
  157. justify-content: space-between;
  158. align-items: center;
  159. span {
  160. font-size: 18px;
  161. font-weight: 600;
  162. }
  163. .switch-link {
  164. font-size: 13px;
  165. color: var(--el-color-primary);
  166. text-decoration: none;
  167. &:hover {
  168. text-decoration: underline;
  169. }
  170. }
  171. }
  172. }
  173. .login-options {
  174. width: 100%;
  175. display: flex;
  176. justify-content: space-between;
  177. align-items: center;
  178. .forgot-password {
  179. font-size: 13px;
  180. color: var(--el-color-primary);
  181. text-decoration: none;
  182. &:hover {
  183. text-decoration: underline;
  184. }
  185. }
  186. }
  187. .login-btn {
  188. width: 100%;
  189. height: 44px;
  190. font-size: 16px;
  191. }
  192. .other-login {
  193. margin-top: 10px;
  194. .social-login {
  195. display: flex;
  196. justify-content: center;
  197. gap: 16px;
  198. }
  199. }
  200. .login-footer {
  201. text-align: center;
  202. margin-top: 24px;
  203. color: rgba(255, 255, 255, 0.7);
  204. font-size: 12px;
  205. }
  206. </style>