Vue项目实战 —— 后台管理系统( pc端 )

前期回顾     

我只写注释 —— 让Ai写代码_0.活在风浪里的博客-CSDN博客前期回顾 Vue项目实战 —— 哔哩哔哩移动端开发—— 第二篇_0.活在风浪里的博客-CSDN博客https://blog.csdn.net/m0_57904695/article/details/123881330在vsCode中下载copilot注册账号,官网GitHub Copilot · Your AI pair programmer等待开通 通知...https://blog.csdn.net/m0_57904695/article/details/123935998?spm=1001.2014.3001.5501

     通用版后台管理系统,如果你是多年的程序猿(媛),你可以巩固一下,,也可以给我提提意见。此典型项目,与企业级开发很是接近!掌握即可领悟雷电,如果你是大学生,它完全可以作为你毕业设计,毕竟你确实写过,项目开发全程采用组件化思想


   话说江湖风起云涌,虽无刀光剑影、却依旧暗藏伏机!程序江湖不进则退,必然需要更加卖力,

目录

适合谁

资料在哪

技术栈有哪些

图例 :

开始

首页架构

1、main 如图展示,配合代码在下面

2、下一步写侧导航 如图

固定数据效果:

 动态数据效果:​

3、根据数据配置路由,menu路由跳转 和 默认显示路由

页面Header

1、封装header

 2、点击折叠按钮实现折叠


适合谁

1 、大学即将毕业 或者 自学前端 缺乏项目经验的
2 、入职以后需要做vue 后台管理系统的
3 、后端开发 没有前端经验 要做vue + java 后台管理项目的 
4、 缺乏vue实战项目经验 基础不是很好的 本教程非常的详细 每一步都总结在md文档里面
参考视频:哔哔哩哩搜索程序员Allen

资料在哪

1、为解决网址问题,项目采用json数据格式,我会将资料发在主页资源《通用后台管理资料》,也可以直接在此博客中复制数据(json)

技术栈有哪些

vue-router + vue + axios + ele + 二次封装axios + mock模拟数据源 + 图表(折、柱、饼),

图例 :

 

 

图例过多不一一展示了,直接开始写

开始

首页架构

1、main 如图展示,主页结构

位置:Main.vue
<template>
  <div id="main">
    <el-container>
      <el-aside width="200px">Aside</el-aside>
      <el-container>
        <el-header>Header</el-header>
        <el-main>Main</el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
export default {
  name: "",
  data() {
    return {};
  },
  methods: {},

  computed: {},
  components: {},
  created() {},
};
</script>

<style lang="scss" scoped>
//@import '引入的css文件';
#main {
  width: 100%;
  height: 100%;
  .el-header,
  .el-footer {
    background-color: #292929;
    text-align: center;
    line-height: 60px;
  }

  .el-aside {
    background-color: #4e5660;
    text-align: center;
    line-height: 200px;
    height: 100vh;
  }

  .el-main {
    background-color: #e9eef3;
    text-align: center;
    line-height: 160px;
  }

  body > .el-container {
    margin-bottom: 40px;
  }

  .el-container:nth-child(5) .el-aside,
  .el-container:nth-child(6) .el-aside {
    line-height: 260px;
  }

  .el-container:nth-child(7) .el-aside {
    line-height: 320px;
  }
}
</style>
位置:router.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
    // 重写 router push 方法, 解决编程式路由往同一地址跳转时会报错的情况
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
    if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
    return originalPush.call(this, location).catch(err => err)
}
const routes = [{
        path: '/', //如果是根路径就重定向到login页面
        redirect: 'login'
    }, {
        //如果浏览器url是'/login'路径,就找 import ('../views/Login.vue')这个页面渲染
        path: '/login',
        name: 'Login',
        meta: {
            title: '后台管理登录',
            requiredPath: false,
        },
        component: () =>
            import ('../views/Login.vue')
    },
    {
        path: '/main', //主测单航页
        name: '',
        component: () =>
            import ('../views/main/Main.vue'),


    }, {
        // 404页面,必须放在最下面,浏览器url找不到对应的路由时,就会跳转到这个页面
        path: "/:pathMatch(.*)",
        component: () =>
            import ("@/views/NotFound.vue"),
    }

]

const router = new VueRouter({
    mode: 'hash',
    routes
})

// 进入之前路由元信息配置守卫 requiredPath为true, 适合守卫多个页面 vue3next() 变成return true
router.beforeEach((to, from, next) => {
        if (sessionStorage.getItem('token')) {
            next()
        } else {
            if (to.meta.requiredPath) { //没有token requiredPath为true 守卫不让进,跳入login
                next('/Login')
            } else {
                next()
            }
        }

    })
    // 导航后置守卫(当你真正进入到某个页面之后才执行)
router.afterEach((to, from) => {
    // 设置路由的标题 (可自定义)
    document.title = to.meta.title || '后台'
        // 将所有的页面切换之后滚动到最顶部
    window.scrollTo(0, 0);
})
export default router

2、下一步写侧导航 如图

 因为这是每个页面都在用,所以我们将他它封装

Aside侧导航,element ui

固定数据效果: 

位置 子组件:components/ComAside.vue
<template>
  <div id="comAside">
    <el-row>
      <el-col>
        <el-menu
          default-active="2"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b"
        >
          <el-menu-item index="2-1">首页</el-menu-item>

          <el-submenu index="1">
            <template slot="title">
              <i class="el-icon-location"></i>
              <span>导航一</span>
            </template>
          </el-submenu>

          <el-submenu index="2">
            <template slot="title">
              <i class="el-icon-location"></i>
              <span>导航2</span>
            </template>

            <el-menu-item-group>
              <el-menu-item index="2-1">选项1</el-menu-item>
              <el-menu-item index="2-2">选项2</el-menu-item>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </el-col>
    </el-row>
  </div>
</template>

<script>
export default {
  name: "",
  data() {
    return {};
  },
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
  },

  computed: {},
  components: {},
  created() {},
};
</script>

<style lang="scss" scoped>
//@import '引入的css文件';
#comAside {
  width: 100%;
  height: 100%;
}
</style>

在父组件引入(main.vue)

     现在结构撘好了,我们需要将死数据,变成接口请求过来的数据 ,看上面的图,因为有的有子菜单,有的则没二级菜单,(也就是数据里的childern属性),所以我们在计算属性里将这不同的两种遍历出来,有二级子菜单,就用咱们遍历有子菜单的,相反则用另一个。

数据: 

 动态数据效果:

位置:components/ComAside.vue
<template>
  <div id="comAside">
    <el-row>
      <el-col>
        <el-menu
          default-active="2"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b"
        >
          <h1
            style="
              font-size: 20px;
              text-align: center;
              color: #fff;
              margin: 15px;
            "
          >
            后台管理系统
          </h1>
          <!-- 没有二级菜单的 -->
          <el-menu-item
            :index="item.path"
            v-for="item in noChildren"
            :key="item.path"
          >
            <i :class="'el-icon-' + item.icon"></i>
            <span slot="title">{
   
   { item.label }}</span>
          </el-menu-item>

          <!-- 有二级菜单的 -->
          <el-submenu
            :index="item.icon"
            v-for="item in hasChildren"
            :key="item.icon"
          >
            <!-- 二级菜单固定文字 -->
            <template slot="title">
              <i :class="'el-icon-' + item.icon"></i>
              <span slot="title">{
   
   { item.label }}</span>
            </template>

            <!-- 二级菜单里的children -->
            <el-menu-item-group
              v-for="subItem in item.children"
              :key="subItem.path"
            >
              <el-menu-item :index="subItem.path">
                <i :class="'el-icon-' + subItem.icon"></i>
                <span slot="title">{
   
   { subItem.label }}</span>
              </el-menu-item>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </el-col>
    </el-row>
  </div>
</template>

<script>
export default {
  name: "",
  data() {
    return {
      menuList: [
        {
          path: "/",
          name: "home",
          label: "首页",
          icon: "s-home",
          url: "Home/Home",
        },
        {
          path: "/mall",
          name: "mall",
          label: "商品管理",
          icon: "video-play",
          url: "MallManage/MallManage",
        },
        {
          path: "/user",
          name: "user",
          label: "用户管理",
          icon: "user",
          url: "UserManage/UserManage",
        },
        {
          label: "其他",
          icon: "location",
          children: [
            {
              path: "/page1",
              name: "page1",
              label: "页面1",
              icon: "setting",
              url: "Other/PageOne",
            },
            {
              path: "/page2",
              name: "page2",
              label: "页面2",
              icon: "edit",
              url: "Other/PageTwo",
            },
          ],
        },
      ],
    };
  },
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
  },

  computed: {
    noChildren() {
      return this.menuList.filter((item) => {
        return !item.children;
      });
    },
    hasChildren() {
      return this.menuList.filter((item) => {
        return item.children;
      });
    },
  },
  components: {},
  created() {},
};
</script>

<style lang="scss" scoped>
//@import '引入的css文件';
#comAside {
  width: 100%;
  height: 100%;
  .el-menu-vertical-demo· {
    border: none;
  }
}
</style>

配合图文,更易理解! 

3、根据数据配置路由,menu路由跳转 和 默认显示路由

效果图:

 

 实现步骤:

后台数据

  

路由配置

路由模块化 

 我的路由分模块了, 

 

完整路由代码(router.js)  

import Vue from "vue";
import VueRouter from "vue-router";
// 引入路由分模块
import Administrator from "./Administrator";
import page from "./page";
Vue.use(VueRouter);
// 重写 router push 方法, 解决编程式路由往同一地址跳转时会报错的情况
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location, onResolve, onReject) {
    if (onResolve || onReject)
        return originalPush.call(this, location, onResolve, onReject);
    return originalPush.call(this, location).catch((err) => err);
};
const routes = [{
        path: "/",
        redirect: "main",
    },
    {
        //如果浏览器url是'/login'路径,就找 import ('@/views/Login.vue')这个页面渲染
        path: "/login",
        name: "Login",
        meta: {
            title: "登录",
            requiredPath: false,
        },
        component: () =>
            import ("@/views/Login.vue"),
    },
    {
        //如果浏览器url是 http://localhost:8080/#/main 路径,就找 import ('@/views/Main/Main.vue')这个页面渲染
        path: "/main", //整个主页结构
        name: "",
        meta: {
            requiredPath: true,
        },
        component: () =>
            import ("@/views/Main/Main.vue"),
        children: [{
                //如果浏览器url是 http://localhost:8080/#/main/ 或者 http://localhost:8080/#/main
                // 就找 import ("@/views/Main/Home") 这个页面渲染
                path: "/",
                name: "",
                meta: {
                    title: "首页",
                    requiredPath: true,
                },
                component: () =>
                    import ("@/views/Main/Home"),
            },
            ...Administrator,
            ...page,
        ],
    },
    {
        // 404页面,必须放在最下面,浏览器url找不到对应的路由时,就会跳转到这个页面
        path: "/:pathMatch(.*)",
        component: () =>
            import ("@/views/NotFound.vue"),
    },
];

const router = new VueRouter({
    // 注意 使用history模式,二级以上页面报错,Uncaught SyntaxError: Unexpected token '<'
    // 需要 将publicPath:"/"或使用hash
    mode: "hash",
    routes,
});

// 进入之前路由元信息配置守卫 requiredPath为true, 适合守卫多个页面 vue3next() 变成return true
// router.beforeEach((to, from, next) => {
//     if (sessionStorage.getItem("token")) {
//         next();
//     } else {
//         if (to.meta.requiredPath) {
//             //没有token requiredPath为true 守卫不让进,跳入login
//             next("/Login");
//         } else {
//             next();
//         }
//     }
// });
// 导航后置守卫(当你真正进入到某个页面之后才执行)
router.afterEach((to, from) => {
    // 设置路由的标题 (可自定义)
    document.title = to.meta.title || "无标题";
    // 将所有的页面切换之后滚动到最顶部
    window.scrollTo(0, 0);
});
export default router;

页面Header

1、封装header

因为在每个页面都要展示,所以封装

效果图:

位置:components/ComHeader.vue
<template>
  <div id="ComHeader">
    <!-- 左侧 -->
    <div class="l-header">
      <!-- 折叠按钮 -->
      <el-button plain icon="el-icon-menu" size="mini" @click="choseBtn">
      </el-button>
      <!-- 面包屑 -->
      <h3 style="color: #fff">首页</h3>
    </div>
    <!-- 右侧 -->
    <div class="r-header">
      <el-dropdown>
        <span class="el-dropdown-link">
          <!-- 用户头像 -->
          <img :src="img" />
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item icon="el-icon-user">个人中心</el-dropdown-item>
          <el-dropdown-item icon="el-icon-switch-button">退出</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      img: require("@/assets/user.png"), // 用户头像
    };
  },
  methods: {
    choseBtn: function () {
      this.$store.commit("aside/setCollapse");
      console.log(0)
    },
  },

  computed: {},
  components: {},
  created() {},
};
</script>

<style lang="scss" scoped>
//@import '引入的css文件';
#ComHeader {
  width: 100%;
  height: 50px;
  padding: 0 15px;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0;

  .l-header {
    display: flex;
    align-items: center;
    justify-content: center;

    .el-button--mini,
    .el-button--mini.is-round {
      padding: 7px 15px;
      margin-right: 15px;
    }
  }

  .r-header {
    display: flex;
    align-items: center;

    ::v-deep el-dropdown {
      color: #fff;
    }

    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
    }
  }
}
</style>

 

 2、点击折叠按钮实现折叠

折叠非常简单,但是我们封装了Aside和Header,点击事件在Header,el-menu在Aside,所以需要兄弟通讯,但是本次我们使用vuex,并且以模块化方式去开发,加大难度

 

 

 

  现在事件和逻辑写好了,下面需要给el-menu将折叠属性绑定上

   取分模块数据也是需要  this.$store.state.模块名.要取得值

  如果取计算属性 this.$store.getters.模块名.要取得值

  详细看  Vue2 vuex贯穿全局,通篇掌握_0.活在风浪里的博客-CSDN博客

              Vue3《Vuex》如此直白_0.活在风浪里的博客-CSDN博客

更新中...

2022/5/15 19:20 

 

猜你喜欢

转载自blog.csdn.net/m0_57904695/article/details/124586612