login.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <template>
  2. <div class="login-page">
  3. <div class="login-container">
  4. <div class="login-content">
  5. <div class="login-header">
  6. <h2>欢迎登录</h2>
  7. <p class="sub-title">请输入您的账号信息</p>
  8. </div>
  9. <a-form
  10. ref="loginForm"
  11. :model="formData"
  12. :rules="rules"
  13. layout="vertical"
  14. >
  15. <a-form-item label="账号" name="username">
  16. <a-input
  17. v-model:value="formData.username"
  18. placeholder="请输入账号"
  19. size="large"
  20. >
  21. <template #prefix>
  22. <UserOutlined style="color: rgba(0,0,0,.25)" />
  23. </template>
  24. </a-input>
  25. </a-form-item>
  26. <a-form-item label="密码" name="password">
  27. <a-input-password
  28. v-model:value="formData.password"
  29. placeholder="请输入密码"
  30. size="large"
  31. >
  32. <template #prefix>
  33. <LockOutlined style="color: rgba(0,0,0,.25)" />
  34. </template>
  35. </a-input-password>
  36. </a-form-item>
  37. <div class="form-options">
  38. <a-checkbox v-model:checked="rememberMe">记住密码</a-checkbox>
  39. <a href="#" class="forgot-password">忘记密码?</a>
  40. </div>
  41. <a-form-item>
  42. <a-button
  43. type="primary"
  44. :loading="loading"
  45. size="large"
  46. block
  47. @click="login"
  48. >
  49. 登录
  50. </a-button>
  51. </a-form-item>
  52. </a-form>
  53. <div class="login-footer">
  54. <p class="register-tip">
  55. 还没有账号?<a href="/register">立即注册</a>
  56. </p>
  57. <div class="other-login">
  58. <div class="divider">
  59. <span>其他登录方式</span>
  60. </div>
  61. <div class="social-login">
  62. <a-tooltip title="微信登录">
  63. <WechatOutlined class="social-icon" />
  64. </a-tooltip>
  65. <a-tooltip title="QQ登录">
  66. <QqOutlined class="social-icon" />
  67. </a-tooltip>
  68. <a-tooltip title="钉钉登录">
  69. <DingdingOutlined class="social-icon" />
  70. </a-tooltip>
  71. </div>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. </template>
  78. <script>
  79. import { defineComponent, reactive, ref } from 'vue';
  80. import { useRouter } from 'vue-router';
  81. import { useStore } from 'vuex';
  82. import {
  83. UserOutlined,
  84. LockOutlined,
  85. WechatOutlined,
  86. QqOutlined,
  87. DingdingOutlined
  88. } from '@ant-design/icons-vue';
  89. export default defineComponent({
  90. name: 'login',
  91. components: {
  92. UserOutlined,
  93. LockOutlined,
  94. WechatOutlined,
  95. QqOutlined,
  96. DingdingOutlined
  97. },
  98. setup() {
  99. const store = useStore();
  100. const router = useRouter();
  101. const loginForm = ref();
  102. const loading = ref(false);
  103. const rememberMe = ref(false);
  104. const formData = reactive({
  105. username: '',
  106. password: ''
  107. });
  108. const rules = {
  109. username: [
  110. { required: true, message: '请输入账号' },
  111. { min: 4, message: '账号长度不能小于4位' }
  112. ],
  113. password: [
  114. { required: true, message: '请输入密码' },
  115. { min: 6, message: '密码长度不能小于6位' }
  116. ]
  117. };
  118. const login = async () => {
  119. try {
  120. loading.value = true;
  121. await loginForm.value.validate();
  122. let permission = [
  123. { type: 'menu', path: '/report', key: '' },
  124. { type: 'btn', path: '/report', key: 'add' },
  125. { type: 'btn', path: '/report', key: 'edit' },
  126. { type: 'btn', path: '/report', key: 'delete' },
  127. { type: 'btn', path: '/report', key: 'export' },
  128. { type: 'menu', path: '/setting/user/add' },
  129. { type: 'menu', path: '/setting/user/edit' },
  130. { type: 'menu', path: '/setting/user/delete' },
  131. { type: 'menu', path: '/setting/role' },
  132. { type: 'menu', path: '/login' },
  133. ];
  134. const auth = {};
  135. permission.filter(item => item.type === 'btn').forEach(item => {
  136. if(auth[item.path]) {
  137. auth[item.path][item.key] = true;
  138. } else {
  139. auth[item.path] = {
  140. [item.key]: true
  141. };
  142. }
  143. });
  144. store.commit('app/setUser', {
  145. user: { id: 1186, name: '张三' },
  146. permission,
  147. auth
  148. });
  149. router.push('/');
  150. } catch (error) {
  151. console.error('登录失败:', error);
  152. } finally {
  153. loading.value = false;
  154. }
  155. };
  156. return {
  157. loginForm,
  158. formData,
  159. rules,
  160. loading,
  161. rememberMe,
  162. login
  163. };
  164. }
  165. });
  166. </script>
  167. <style lang="less" scoped>
  168. .login-page {
  169. display: flex;
  170. justify-content: center;
  171. align-items: center;
  172. min-height: 100vh;
  173. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  174. .login-container {
  175. width: 100%;
  176. max-width: 420px;
  177. margin: 0 20px;
  178. }
  179. .login-content {
  180. background: rgba(255, 255, 255, 0.95);
  181. padding: 40px;
  182. border-radius: 16px;
  183. box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
  184. backdrop-filter: blur(8px);
  185. animation: slideUp 0.6s ease-out;
  186. }
  187. .login-header {
  188. text-align: center;
  189. margin-bottom: 32px;
  190. h2 {
  191. font-size: 28px;
  192. color: #1a1a1a;
  193. margin-bottom: 8px;
  194. font-weight: 600;
  195. }
  196. .sub-title {
  197. color: #666;
  198. font-size: 14px;
  199. }
  200. }
  201. .form-options {
  202. display: flex;
  203. justify-content: space-between;
  204. align-items: center;
  205. margin-bottom: 24px;
  206. .forgot-password {
  207. color: #667eea;
  208. font-size: 14px;
  209. transition: color 0.3s;
  210. &:hover {
  211. color: #764ba2;
  212. }
  213. }
  214. }
  215. .login-footer {
  216. margin-top: 32px;
  217. text-align: center;
  218. .register-tip {
  219. color: #666;
  220. font-size: 14px;
  221. margin-bottom: 24px;
  222. a {
  223. color: #667eea;
  224. font-weight: 500;
  225. margin-left: 4px;
  226. &:hover {
  227. color: #764ba2;
  228. }
  229. }
  230. }
  231. .divider {
  232. position: relative;
  233. text-align: center;
  234. margin: 16px 0;
  235. &::before,
  236. &::after {
  237. content: '';
  238. position: absolute;
  239. top: 50%;
  240. width: 30%;
  241. height: 1px;
  242. background: #e8e8e8;
  243. }
  244. &::before {
  245. left: 0;
  246. }
  247. &::after {
  248. right: 0;
  249. }
  250. span {
  251. background: #fff;
  252. padding: 0 12px;
  253. color: #999;
  254. font-size: 12px;
  255. }
  256. }
  257. .social-login {
  258. display: flex;
  259. justify-content: center;
  260. gap: 24px;
  261. margin-top: 16px;
  262. .social-icon {
  263. font-size: 24px;
  264. color: #666;
  265. cursor: pointer;
  266. transition: all 0.3s;
  267. &:hover {
  268. color: #667eea;
  269. transform: scale(1.1);
  270. }
  271. }
  272. }
  273. }
  274. }
  275. @keyframes slideUp {
  276. from {
  277. opacity: 0;
  278. transform: translateY(20px);
  279. }
  280. to {
  281. opacity: 1;
  282. transform: translateY(0);
  283. }
  284. }
  285. // 响应式适配
  286. @media (max-width: 480px) {
  287. .login-content {
  288. padding: 24px;
  289. }
  290. .login-header h2 {
  291. font-size: 24px;
  292. }
  293. }
  294. </style>