vue3-admin 手把手教大家如何搭建后台管理系统(二)
说明步骤
- 规划整体布局
- 规划侧边菜单栏
- 规划头部
- 规划主体
那么显而易见就是新建一个 layout 目录,建立三个组件
layout/components/GiAside.vue 侧边栏上面项目的头像,还有菜单,菜单具有默认选择项,展开收起功能
<template>
<a-layout-sider collapsible breakpoint="xl" class="gi-aside">
<div class="logo" />
<a-menu :defaultSelectedKeys="[activePath]" :style="{ width: '100%' @menuItemClick="onClickMenuItem">
<a-menu-item v-for="item in menuList" :key="item.path" @click="handleClickItem(item)">
<component :is="item.icon"></component>
{{ item.name }}
</a-menu-item>
</a-menu>
<template #trigger="{ collapsed }">
<IconCaretRight v-if="collapsed" />
<IconCaretLeft v-else />
</template>
</a-layout-sider>
</template>
接着我们需要从 vuex 上面拿到菜单数据,获取激活状态的默认路由
<script lang="ts" setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
import { IconCaretRight, IconCaretLeft, IconHome, IconCalendar } from '@arco-design/web-vue/es/icon'
const store = useStore()
const router = useRouter()
console.log('store', store)
// 获取菜单
const menuList = computed(() => store.state.app.menuList)
// 获取激活的路劲
const activePath = computed(() => store.state.app.activePath)
// 点击触发 store
const handleClickItem = (item) => {
// 设置激活的路由 store
store.commit('app/storeSetActivePath', item.path)
// 进行跳转
router.push(item.path)
}
</script>
layout/components/GiHeader.vue
头部组件需要有 tag 标签,以及点击头像出现个人中心,退出登录,以及切换主题色
<template>
<a-layout-header>
<section class="sys-name">管理系统</section>
<section class="sys-head">
<!-- 改变主题色 -->
<a-button size="mini" @click="changeTheme" :style="{ marginRight: '10px' }">
<template #icon>
<icon-sun-fill v-if="light" />
<icon-moon-fill v-else />
</template>
</a-button>
<!-- 头像 -->
<a-avatar :size="32" :style="{ marginRight: '8px' }">A</a-avatar>
<a-dropdown trigger="hover">
<a-button type="text">admin</a-button>
<template #content>
<a-doption>
<template #icon><icon-user /></template><span style="margin-left: 4px">个人中心</span>
</a-doption>
<a-doption @click="logout">
<template #icon><icon-export /></template><span style="margin-left: 4px">退出登录</span>
</a-doption>
</template>
</a-dropdown>
</section>
</a-layout-header>
</template>
接着实现对应的功能,切换主题色的功能,可以参考 arco.design/vue/docs/da…
<script lang="ts" setup>
import { computed, ref } from '@vue/reactivity'
import { Modal } from '@arco-design/web-vue'
import { useRouter } from 'vue-router'
const router = useRouter()
let light = ref('')
const changeTheme = () => {
let theme = document.body.getAttribute('arco-theme')
light.value = theme
if(!theme) {
// 设置暗黑主题
document.body.setAttribute('arco-theme', 'dark')
} else {
document.body.removeAttribute('arco-theme')
}
}
</script>
layout/components/GiMain.vue
接下来就是实现主体的页面
<template>
<a-layout class="gi-main">
<!-- <router-view #default="{ Component }">
<transition name="fade-transform" class="abs">
<component :is="Component" :key="key" />
</transition>
</router-view> -->
<router-view></router-view>
</a-layout>
</template>
脚本如下,很简单吧
<script lang="ts" setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const key = computed(() => route.path)
</script>
然后我们需要在 layout 下面新建一个 index.vue
layout/index.vue 将页面在整体框架上面使用
<template>
<a-layout class="gi-layout">
<GiAside></GiAside>
<a-layout>
<GiHeader></GiHeader>
<GiMain></GiMain>
</a-layout>
</a-layout>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { Message } from '@arco-design/web-vue'
import { IconCaretRight, IconCaretLeft, IconHome, IconCalendar } from '@arco-design/web-vue/es/icon'
import GiAside from './components/GiAside.vue'
import GiHeader from './components/GiHeader.vue'
import GiMain from './components/GiMain.vue'
export default defineComponent({
components: {
GiAside,
GiHeader,
GiMain,
IconCaretRight,
IconCaretLeft,
IconHome,
IconCalendar
},
methods: {
onClickMenuItem(key) {
Message.info({ content: `You select ${key}`, showIcon: true })
}
}
})
</script>
接下来就是实现我们的路由
router/index.ts,这个没啥好看的,直接复制就行了
// 引入vue-router对象
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/login/index.vue')
},
{
path: '/',
redirect: '/home',
component: () => import('@/layout/index.vue'),
children: [
{
path: '/home',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: { title: '首页', keepAlive: false }
},
{
path: '/indicator-manage',
name: 'IndicatorManage',
component: () => import('@/views/indicator-manage/main/index.vue'),
meta: { title: '指标管理', keepAlive: false }
},
{
path: '/indicator-manage/detail',
name: 'IndicatorManageDetail',
component: () => import('@/views/indicator-manage/detail/index.vue'),
meta: { title: '指标管理-详情', keepAlive: false }
},
{
path: '/user',
name: 'user',
component: () => import('@/views/user/index.vue'),
meta: { title: '用户中心', keepAlive: false }
}
]
}
]
/**
* 创建路由
*/
const router = createRouter({
// hash模式:createWebHashHistory,
// history模式:createWebHistory
history: createWebHistory('/'),
// history:createWebHashHistory(),
routes
})
/**
* 路由守卫
*/
// router.beforeEach((guard) => {
// beforeEach.checkAuth(guard, router)
// })
/**
* 路由错误回调
*/
router.onError((handler) => {
console.log('error:', handler)
})
/**
* 输出对象
*/
export default router
store/modules/app.ts
const activePath = localStorage.getItem('ActivePath') as string
const state = {
systemName: 'GI数据采集管理系统', // 系统名称
activePath: JSON.parse(activePath) || '/home', // 当前激活的路径
menuList: [
{
icon: 'IconRobot',
id: 'GZT',
name: '工作台',
path: '/home'
},
{
icon: 'IconCopyright',
id: 'ZBGL',
name: '指标管理',
path: '/indicator-manage'
},
{
icon: 'IconSettings',
id: 'ZBGL',
name: '个人中心',
path: '/user'
}
]
}
// 获取菜单
const getters = {
storeGetMenuList(state) {
return state.menuList
}
}
// 设置菜单
const mutations = {
// 设置激活路径地址
storeSetActivePath(state, path) {
state.activePath = path
localStorage.setItem('ActivePath', JSON.stringify(path))
}
}
// 暴露模块
export default {
namespaced: true,
state,
getters,
mutations
}
store/modules/user.ts
// 用户信息
const state = {
user: JSON.parse(localStorage.getItem('USER') as string) || {
account: '',
deptId: '',
email: '',
id: '',
isAdmin: false,
name: '',
phone: ''
}
}
const getters = {
// 获取用户信息
storeUser(state) {
return state.user
}
}
const mutations = {
// 设置用户信息
storeSetUser(state, userInfo) {
const { account, deptId, email, id, isAdmin, name, phone } = userInfo
state.user = { account, deptId, email, id, isAdmin, name, phone }
localStorage.setItem('USER', JSON.stringify(state.user))
}
}
const actions = {}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
store/index.ts
import { createStore } from 'vuex'
import app from './modules/app'
import user from './modules/user'
export const store = createStore({
modules: {
app,
user
}
})
export default store
./plugins/arco-vue-plugin.ts
import ArcoVue from '@arco-design/web-vue'
// 额外引入图标库
import ArcoVueIcon from '@arco-design/web-vue/es/icon'
export const setArcoVue = (app) => {
app.use(ArcoVue)
app.use(ArcoVueIcon)
}
配置 main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@arco-design/web-vue/dist/arco.css'
import { setArcoVue } from './plugins/arco-vue-plugin'
import '@/styles/index.scss' // 导入scss主文件
const app = createApp(App)
// 路由要先注册
app.use(router)
app.use(store)
setArcoVue(app)
app.mount('#app')