vue3-admin 手把手教大家如何搭建后台管理系统(二)

1,601

vue3-admin 手把手教大家如何搭建后台管理系统(二)

说明步骤

  1. 规划整体布局
  2. 规划侧边菜单栏
  3. 规划头部
  4. 规划主体

那么显而易见就是新建一个 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')