效果

image.png

准备

背景图svg

准备body.svg

src/assets/body.svg
image.png

注册相关element组件

image.png
src/plugins/element.ts

  1. import { App } from 'vue'
  2. import {
  3. locale,
  4. ElButton,
  5. ElMessage,
  6. ElNotification,
  7. ElMessageBox,
  8. ElMenu,
  9. ElMenuItem,
  10. ElSubmenu,
  11. ElRow,
  12. ElCol,
  13. ElBreadcrumb,
  14. ElBreadcrumbItem,
  15. ElTooltip,
  16. ElDropdown,
  17. ElDropdownMenu,
  18. ElDropdownItem,
  19. ElScrollbar,
  20. ElDrawer,
  21. ElColorPicker,
  22. ElSwitch,
  23. ElForm,
  24. ElFormItem,
  25. ElInput
  26. } from 'element-plus'
  27. // 默认主题
  28. // import 'element-plus/lib/theme-chalk/index.css'
  29. // Element Plus 组件内部默认使用英语
  30. // https://element-plus.gitee.io/#/zh-CN/component/i18n
  31. import lang from 'element-plus/lib/locale/lang/zh-cn'
  32. // Element Plus 直接使用了 Day.js 项目的时间日期国际化设置, 并且会自动全局设置已经导入的 Day.js 国际化配置。
  33. import 'dayjs/locale/zh-cn'
  34. // $ELEMENT size属性类型
  35. export type Size = 'default' | 'medium' | 'small' | 'mini'
  36. interface ElementOptions {
  37. size: Size
  38. }
  39. export default (app: App, options: ElementOptions): void => {
  40. locale(lang)
  41. // 按需导入组件列表
  42. const components = [
  43. ElButton,
  44. ElMessage,
  45. ElNotification,
  46. ElMessageBox,
  47. ElMenu,
  48. ElMenuItem,
  49. ElSubmenu,
  50. ElRow,
  51. ElCol,
  52. ElBreadcrumb,
  53. ElBreadcrumbItem,
  54. ElTooltip,
  55. ElDropdown,
  56. ElDropdownMenu,
  57. ElDropdownItem,
  58. ElScrollbar,
  59. ElDrawer,
  60. ElColorPicker,
  61. ElSwitch,
  62. ElForm,
  63. ElFormItem,
  64. ElInput
  65. ]
  66. components.forEach(component => {
  67. app.component(component.name, component)
  68. })
  69. // Vue.prototype 替换为 config.globalProperties
  70. // 文档说明 https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-prototype-%E6%9B%BF%E6%8D%A2%E4%B8%BA-config-globalproperties
  71. app.config.globalProperties.$message = ElMessage
  72. app.config.globalProperties.$notify = ElNotification
  73. app.config.globalProperties.$confirm = ElMessageBox.confirm
  74. app.config.globalProperties.$alert = ElMessageBox.alert
  75. app.config.globalProperties.$prompt = ElMessageBox.prompt
  76. // 全局配置 https://element-plus.gitee.io/#/zh-CN/component/quickstart#quan-ju-pei-zhi
  77. // 该对象目前支持 size 与 zIndex 字段。size 用于改变组件的默认尺寸 small,zIndex 设置弹框的初始 z-index(默认值:2000)。
  78. app.config.globalProperties.$ELEMENT = {
  79. size: options.size
  80. }
  81. }

注册登录路由

image.png
src/router/index.ts

注册login路由 放到constantRoutes集合里

  1. import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
  2. import Layout from '@/layout/index.vue'
  3. // 看作是异步获取路由
  4. export const asyncRoutes: Array<RouteRecordRaw> = [
  5. {
  6. path: '/documentation',
  7. component: Layout, // 布局组件作为一级路由
  8. redirect: '/documentation/index',
  9. children: [
  10. {
  11. path: 'index',
  12. name: 'Documentation',
  13. component: () => import(/* webpackChunkName: "documentation" */ '@/views/documentation/index.vue'),
  14. meta: {
  15. title: 'Documentation',
  16. icon: 'documentation',
  17. hidden: false, // 菜单栏不显示
  18. // 路由是否缓存 没有这个属性或false都会缓存 true不缓存
  19. noCache: false
  20. }
  21. }
  22. ]
  23. },
  24. {
  25. path: '/guide',
  26. component: Layout,
  27. redirect: '/guide/index',
  28. children: [
  29. {
  30. path: 'index',
  31. name: 'Guide',
  32. component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),
  33. meta: {
  34. title: 'Guide',
  35. icon: 'guide'
  36. // 当guide路由激活时高亮选中的是 documentation/index菜单
  37. // activeMenu: '/documentation/index'
  38. }
  39. }
  40. ]
  41. },
  42. {
  43. path: '/system',
  44. component: Layout,
  45. redirect: '/system/user',
  46. meta: {
  47. title: 'System',
  48. icon: 'lock',
  49. alwaysShow: true // 根路由始终显示 哪怕只有一个子路由
  50. },
  51. children: [
  52. {
  53. path: 'menu',
  54. name: 'Menu Management',
  55. component: () => import(/* webpackChunkName: "menu" */ '@/views/system/menu.vue'),
  56. meta: {
  57. title: 'Menu Management',
  58. hidden: false,
  59. breadcrumb: false
  60. }
  61. },
  62. {
  63. path: 'role',
  64. name: 'Role Management',
  65. component: () => import(/* webpackChunkName: "role" */ '@/views/system/role.vue'),
  66. meta: {
  67. title: 'Role Management',
  68. hidden: false
  69. }
  70. },
  71. {
  72. path: 'user',
  73. name: 'User Management',
  74. component: () => import(/* webpackChunkName: "user" */ '@/views/system/user.vue'),
  75. meta: {
  76. title: 'User Management'
  77. }
  78. }
  79. ]
  80. },
  81. { // 外链路由
  82. path: '/external-link',
  83. component: Layout,
  84. children: [
  85. {
  86. path: 'https://www.baidu.com/',
  87. redirect: '/',
  88. meta: {
  89. title: 'External Link',
  90. icon: 'link'
  91. }
  92. }
  93. ]
  94. },
  95. { // 404一定放在要在最后面
  96. path: '/:pathMatch(.*)*',
  97. redirect: '/404',
  98. meta: {
  99. hidden: true
  100. }
  101. }
  102. ]
  103. export const constantRoutes: Array<RouteRecordRaw> = [
  104. {
  105. path: '/',
  106. component: Layout,
  107. redirect: '/dashboard',
  108. children: [
  109. {
  110. path: 'dashboard',
  111. name: 'Dashboard',
  112. component: () => import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/index.vue'),
  113. meta: {
  114. title: 'Dashboard',
  115. // icon: 'dashboard'
  116. icon: 'el-icon-platform-eleme',
  117. affix: true // 固定显示在tagsView中
  118. }
  119. }
  120. ]
  121. },
  122. {
  123. path: '/redirect',
  124. component: Layout,
  125. meta: {
  126. hidden: true
  127. },
  128. children: [
  129. { // 带参数的动态路由正则匹配
  130. // https://next.router.vuejs.org/zh/guide/essentials/route-matching-syntax.html#%E5%8F%AF%E9%87%8D%E5%A4%8D%E7%9A%84%E5%8F%82%E6%95%B0
  131. path: '/redirect/:path(.*)', // 要匹配多级路由 应该加*号
  132. component: () => import('@/views/redirect/index.vue')
  133. }
  134. ]
  135. },
  136. {
  137. path: '/login',
  138. name: 'Login',
  139. component: () => import('@/views/login/index.vue')
  140. },
  141. {
  142. path: '/401',
  143. component: Layout,
  144. children: [
  145. {
  146. path: '',
  147. component: () => import('@/views/error-page/401.vue'),
  148. meta: {
  149. title: '401',
  150. icon: '404',
  151. hidden: true
  152. }
  153. }
  154. ]
  155. },
  156. {
  157. path: '/404',
  158. component: () => import('@/views/error-page/404.vue'),
  159. meta: {
  160. hidden: true // 404 hidden掉
  161. }
  162. }
  163. ]
  164. export const routes = [
  165. ...constantRoutes,
  166. ...asyncRoutes
  167. ]
  168. const router = createRouter({
  169. history: createWebHashHistory(),
  170. routes
  171. })
  172. export default router

创建login路由

src/views/login/index.vue

  1. <template>
  2. <div class="login-container">
  3. <el-form
  4. class="login-form"
  5. >
  6. <div class="admin-logo">
  7. <img class="logo" src="../../assets/logo.png" alt="logo">
  8. <h1 class="name">Vue3 Admin</h1>
  9. </div>
  10. <el-form-item prop="username">
  11. <span class="svg-container">
  12. <svg-icon icon-class="user"></svg-icon>
  13. </span>
  14. <el-input
  15. placeholder="请输入用户名"
  16. />
  17. </el-form-item>
  18. <el-form-item>
  19. <span class="svg-container">
  20. <svg-icon icon-class="password"></svg-icon>
  21. </span>
  22. <el-input
  23. placeholder="请输入密码"
  24. autocomplete="on"
  25. />
  26. </el-form-item>
  27. <!-- 登录按钮 -->
  28. <el-button
  29. type="primary"
  30. style=" width: 100%; margin-bottom: 30px"
  31. :loading="loading"
  32. @click="handleLogin"
  33. >Login</el-button>
  34. </el-form>
  35. </div>
  36. </template>
  37. <script lang="ts">
  38. import { defineComponent, ref } from 'vue'
  39. export default defineComponent({
  40. name: 'Login',
  41. setup() {
  42. const loading = ref(false)
  43. const handleLogin = () => {
  44. console.log('login')
  45. }
  46. return {
  47. loading,
  48. handleLogin
  49. }
  50. }
  51. })
  52. </script>
  53. <style lang="scss">
  54. $bg:#283443;
  55. $light_gray:#fff;
  56. $cursor: #fff;
  57. .login-container {
  58. .el-form-item {
  59. border: 1px solid #dcdee2;
  60. border-radius: 5px;
  61. padding-left: 10px;
  62. .el-input {
  63. display: inline-block;
  64. height: 40px;
  65. width: 85%;
  66. input {
  67. background: transparent;
  68. border: 0;
  69. -webkit-appearance: none;
  70. border-radius: 0px;
  71. padding: 12px 5px 12px 15px;
  72. height: 40px;
  73. }
  74. }
  75. }
  76. }
  77. </style>
  78. <style lang="scss" scoped>
  79. $bg:#2d3a4b;
  80. $dark_gray:#889aa4;
  81. $light_gray:#eee;
  82. .login-container {
  83. min-height: 100%;
  84. width: 100%;
  85. overflow: hidden;
  86. background-image: url('../../assets/body.svg');
  87. background-repeat: no-repeat;
  88. background-position: 50%;
  89. background-size: 100%;
  90. .login-form {
  91. position: relative;
  92. width: 500px;
  93. max-width: 100%;
  94. margin: 0 auto;
  95. padding: 140px 35px 0;
  96. overflow: hidden;
  97. box-sizing: border-box;
  98. .admin-logo {
  99. display: flex;
  100. align-items: center;
  101. justify-content: center;
  102. margin-bottom: 20px;
  103. .logo {
  104. width: 60px;
  105. height: 60px;
  106. }
  107. .name {
  108. font-weight: normal;
  109. margin-left: 10px;
  110. }
  111. }
  112. }
  113. }
  114. </style>

本节参考源码

https://gitee.com/brolly/vue3-element-admin/commit/b923a1ad3a37371bd9b99e1838e2e96346857186