✅01-初始化 Vite 项目
✅02-配置 Vite2 环境变量
✅03-Vite2 配置及说明
✅04-Vue3 使用 SCSS
✅05-Vue3 路由配置
✅06-TypeScript 配置及说明
✅07-Vue3 使用 axios
✅08-Vue3 axios 对象封装
✅09-ESLint 配置及说明
✅10-ESLint 与 Prettier 集成配置及说明
✅11-Mock.js 模拟接口数据
✅12-Vite2 引入 Element-Plus 框架
✅13-渐变+透明样式实现清爽登录页
✅14-Element-Plus 实现后台管理系统布局
✅15-Pinia 实现 store 状态管理
✅16-Vue3 动态路由权限控制
源码地址:GitHub / 码云
完成 Header、Sidebar、Footer 开发,mock 菜单和用户信息的接口数据。实现菜单收缩、展开、用户登出功能。
npm install -S @element-plus/icons
在 src/components/layout
目录下创建 header、sidebar、footer
文件夹,用来布局用到的各子组件。
layout文件目录结构如下:
src
---- components
-------- layout
------------ footer
---------------- index.ts
------------ header
---------------- index.ts
------------ sidebar
---------------- index.ts
------------ index.ts
只粘贴了部分核心代码,完整代码可去 GitHub / 码云 获取
在 src/api/login/index.ts
添加查询用户信息接口:
import http from '@/utils/http/index'
export default {
// ↓登录
signin: (data?: any) => {
return http.post('/login/signin', data)
},
// ↓查询用户信息
userInfo: () => {
return http.get('/login/userInfo')
},
}
在 src/mock/login/index.ts
mock 查询用户信息接口数据:
import Mock from 'mockjs'
// ↓mock数据
const data = Mock.mock({
'info|1': [
{
baseInfo: {
id: '@INCREMENT()',
username: 'admin',
name: '超级管理员',
avatar: '@IMAGE(100, "#ffc72d", "Code-Bee"),',
},
menus: [
{
id: 1,
name: '权限管理',
icon: 'el-icon-menu',
children: [
{ id: 2, name: '用户管理', path: '/sys/user' },
{ id: 3, name: '角色管理', path: '/sys/role' },
{ id: 4, name: '菜单管理', path: '/sys/menu' },
],
},
{
id: 5,
name: '系统管理',
icon: 'el-icon-setting',
children: [
{ id: 6, name: '系统字典', path: '/sys/dict' },
{ id: 7, name: '参数配置', path: '/sys/config' },
{ id: 8, name: '通知公告', path: '/sys/notice' },
{ id: 9, name: '日志审计', path: '/sys/log' },
],
},
],
},
],
})
export default [
// ↓登录
{
url: new RegExp('/login/signin'),
type: 'post',
result: (config: any) => {
const obj = JSON.parse(config.body)
// ↓校验用户名密码
if (obj.username !== 'admin' || obj.password !== '123456') {
return {
code: 400,
data: null,
message: '用户名或者密码错误',
}
}
return {
code: 200,
data: data.info,
message: '登录成功',
}
},
},
// ↓查询用户信息
{
url: new RegExp('/login/userInfo'),
type: 'get',
result: (config: any) => {
return {
code: 200,
data: data.info,
message: '登录成功',
}
},
},
]
修改 src/components/layout/index.vue
,引入封装好的其它子组件:
layout、sidebar、header 用到父子组件间的传值与修改
<template>
<el-container>
<Sidebar />
<el-container>
<el-header height="50px"><Header v-model:sidebar-collapse="sidebarCollapse" /></el-header>
<el-main>
<!-- ↓layout路由视图 -->
<router-view></router-view>
</el-main>
<Footer />
</el-container>
</el-container>
</template>
<script>
import { defineComponent, ref, provide } from 'vue'
import Sidebar from './sidebar/index.vue'
import Header from './header/index.vue'
import Footer from './footer/index.vue'
export default defineComponent({
name: 'Layout',
components: { Sidebar, Header, Footer },
setup() {
// ↓侧边栏折叠
const sidebarCollapse = ref(false)
// ↓提供给sidebar注入
provide('sidebarCollapse', sidebarCollapse)
return {
sidebarCollapse,
}
},
})
</script>
在 src/components/layout/sidebar/index.vue
中编写菜单和 Logo ,数据 mock 获取:
<template>
<div class="sidebar">
<el-menu default-active="1-1" class="menu" :collapse="sidebarCollapse">
<div class="logo">
<img src="@/assets/logo-100.png" width="32" height="32" />
<span v-show="!sidebarCollapse"> Code-Bee管理系统</span>
</div>
<el-sub-menu v-for="menu in userInfo.menus" :key="menu.id" :index="menu.id + ''">
<template #title>
<i :class="menu.icon"></i>
<span>{{ menu.name }}</span>
</template>
<el-menu-item v-for="sub in menu.children" :key="sub.id" :index="sub.id + ''">{{ sub.name }}</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, inject, onMounted, reactive, ref } from 'vue'
import loginApi from '@/api/login'
export default defineComponent({
name: 'Sidebar',
setup() {
// ↓注入父组件值
const sidebarCollapse = ref(inject('sidebarCollapse'))
// TODO 用户信息变量,后续改成从store获取变量
const userInfo = reactive({
menus: [],
})
onMounted(() => {
// ↓查询用户信息
loginApi.userInfo().then((res: any) => {
const { menus } = res.data
userInfo.menus = menus
})
})
return {
sidebarCollapse,
userInfo,
}
},
})
</script>
在 src/components/layout/header/index.vue
中编写侧边栏收缩开关及用户中心,数据 mock 获取:
<template>
<div class="header">
<div class="fold-icon" @click="toggle">
<el-icon v-if="!sidebarCollapse" :size="24" color="#909399">
<fold />
</el-icon>
<el-icon v-else :size="24" color="#909399">
<expand />
</el-icon>
</div>
<el-dropdown trigger="click">
<div class="avatar">
<el-avatar :src="userInfo.baseInfo.avatar"></el-avatar>
<el-icon>
<caret-bottom />
</el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="el-icon-user">个人中心</el-dropdown-item>
<el-dropdown-item icon="el-icon-right" divided @click="signout">退出用户</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, reactive } from 'vue'
import { useRouter } from 'vue-router'
import loginApi from '@/api/login'
export default defineComponent({
name: 'Header',
props: {
sidebarCollapse: {
type: Boolean,
default: false,
},
},
emits: ['update:sidebarCollapse'],
setup(props, { emit }) {
const router = useRouter()
// TODO 用户信息变量,后续改成从store获取变量
const userInfo = reactive({
baseInfo: {},
})
// ↓sidebar折叠/展开的开关
const toggle = () => {
// ↓修改父组件值
emit('update:sidebarCollapse', !props.sidebarCollapse)
}
// ↓登出
const signout = () => {
// TODO 有store变量后需清除store变量
router.push('/login')
}
onMounted(() => {
// ↓查询用户信息
loginApi.userInfo().then((res: any) => {
const { baseInfo } = res.data
userInfo.baseInfo = baseInfo
})
})
return { userInfo, toggle, signout }
},
})
</script>
本文为博主原创文章,任何个人、团体、机构转载和摘录,请注明出处。