配合目录使用更加友好哦,文章中分享的项目搭建是完全从0-1搭建,完全适用于小白,可用于vue练手项目,目前还在持续更新中,本篇文章不会断更,因工作原因,只能晚上给大家更新,感觉还行的可以给个关注或者收藏本篇文章进行学习!!

文章目录

  • 一、vue通用后台管理系统分析
    • 技术栈
    • 项目搭建
    • 模块分配
  • 二、脚手架搭建项目
    • 1、项目环境搭建
      • 1.1nodejs环境(有的话可跳过)
        • 1.1.1 下载
        • 1.1.2下载你完成后傻瓜式安装(具体可以网上找教程哦)
        • 1.1.3安装完成后可进入终端查看一下node是否安装成功
      • 2.1安装cli
        • 2.1.1使用vue - v查看vue的版本,检验是否安装成功。
        • 2.1.3使用cli创建项目
        • 2.1.4选择好之后开始创建
        • 2.1.5创建完成后切换到项目文件夹启动项目
        • 2.1.6cli脚手架项目创建完成
  • 三、在vue中使用element-ui
    • element-ui结合脚手架使用之全部引用
    • 1.安装element-ui
      • 1.1 npm 安装
      • 2.1package.json介绍
    • 2.使用element-ui
      • 2.1完整引入 Element-ui
      • 2.2按需引入 Element-ui
        • 扩展(element-ui文档)
  • 四、在vue中引入vue-router
    • 1.npm安装vue-router
    • 2.vue-router路由配置(上)
      • 2.1vue-router路由配置代码
        • 2.1.1.创建路由页面组件
        • 2.1.2 配置路由文件
        • 2.1.3路由出口
        • 扩展 eslint
    • 3.vue-router路由配置(下)
      • 3.1vue-router中的嵌套路由
        • 3.1.1 项目子路由配置
          • 主页面组件
  • 五、vue通用管理后台(整体UI搭建)
    • 1、通用管理后台(布局)
      • 1.1、使用 Container 布局容器进行布局
    • 2、通用管理后台(完善左侧菜单栏)
      • 2.1、使用 NavMenu 导航菜单布局容器进行完善左侧菜单栏
    • 3、通用管理后台(完善菜单)
      • 3.1 、修改菜单默认收起选项
      • 3.2、修改菜单的结构
    • 4、通用管理后台(根据菜单数据匹配相关页面)
      • 4.1、menu菜单数据定义过滤
      • 4.2、一级动态菜单生成
      • 4.3、二级动态菜单生成
      • 4.4、菜单样式与less引入
        • 4.4.1、less使用和引入
      • 4.5、菜单点击跳转功能实现
        • 4.5.1、根据菜单数据,我们先定义一下我们未定义的路由**
        • 4.5.2、点击菜单,根据点击菜单的数据跳转应路由页面**
    • 5、通用管理后台(header组件与样式搭建)
      • 5.1、封装header区域组件与样式
      • 5.2、通过vuex实现左侧折叠
        • 5.2.1、什么是vuex?
        • 5.2.2、安装vuex
        • 5.2.3、配置vuex
        • 5.2.4、使用vuex
      • 5.3、解决菜单收缩展开的bug
    • 5、通用管理后台(home组件与样式搭建)
      • 5.1、home组件布局
        • 5.1.1、home组件--左侧用户信息
        • 5.1.2、home组件--左侧table表格信息
        • 5.1.3、home组件--右侧学习数据统计实现
    • 6、通用管理后台(Axios 和Mock.js模拟数据)
      • 6.1、扩展--前后端联调
      • 6.2、Axios
        • 6.2.1、Axios 的基本使用
        • 6.2.2、Axios 的二次封装
        • 6.2.3、api调用接口文件封装
      • 6.3、Mock.js
        • 6.3.1、Mock.js安装
        • 6.3.2、Mock.mock()方法
        • 6.3.3、Mock使用
        • 6.3.4、Mock模拟接口数据
    • 7、通用管理后台(首页可视化图表样式调整)
      • 7.1、首页table由静态数据切换成动态数据**
      • 7.2、样式完善和图标表区域布局
        • 7.2.1、Echarts
        • 7.2.1、折线图部分完善
        • 7.2.2、柱状图部分完善
        • 7.2.3、饼图部分完善
    • 8、通用管理后台(面包屑和tag部分)
  • 总结
    • 持续更新中

一、vue通用后台管理系统分析

  1. 项目搭建+使用element-ui实现首页布局
  2. 顶部导航菜单及与左侧导航联动的面包屑实现
  3. 封装一个ECharts组件
  4. 封装一个Form表单组件和Table表格组件
  5. 企业开发之权限管理思路讲解

技术栈

  • vue-router
  • vuex
  • axios
  • element-ui
  • 二次封装axios
  • mock
  • echarts

项目搭建

  • 项目架构分析
  • 项目模块搭建
  • 脚手架搭建配置
  • 组件初始化
  • 路由初始化
  • vuex初始化

模块分配

  • 登录页
  • 后台首页
  • 用户管理页
  • 分页处理
  • 用户crad
  • 路由守卫
  • 权限管理

二、脚手架搭建项目

可参考这篇文章=> Vue脚手架(cli和vite详解)
vue-cli官网

1、项目环境搭建

1.1nodejs环境(有的话可跳过)

环境需要 要先使用npm进行管理,而使用npm需要先下载nodejs。
☞Nodejs下载地址中文官网

1.1.1 下载

根据自己电脑系统及位数选择,我的电脑是Windows系统、64位、想下载稳定版的.msi(LTS为长期稳定版)这里选择windows64位.msi格式安装包。

1.1.2下载你完成后傻瓜式安装(具体可以网上找教程哦)

1.1.3安装完成后可进入终端查看一下node是否安装成功
node -v # 查看是否安装成功 node.js
npm -v

在前面设置好nodejs相关的配置之后,我们可以直接使用npm工具拉取vue-cli脚手架## 1、cli脚手架创建项目步骤

2.1安装cli

npm install -g  @vue/cli  #安装npm uninstall -g vue-cli # 卸载

2.1.1使用vue - v查看vue的版本,检验是否安装成功。
vue -v  或者 vue -version #查看cli脚手架版本

2.1.3使用cli创建项目
vue create 项目名  #创建项目


第三个Manually select features是自定义选择
我们这个项目就不选择自定义了,选择 第二个Default ([Vue 2] babel, eslint)创建vue2项目即可

2.1.4选择好之后开始创建

2.1.5创建完成后切换到项目文件夹启动项目


2.1.6cli脚手架项目创建完成

三、在vue中使用element-ui

element-ui网址

element-ui结合脚手架使用之全部引用

1.安装element-ui

1.1 npm 安装

安装前,记得cd进入到我们创建的项目文件夹中

npm i element-ui -S

2.1package.json介绍


element-ui安装之后,会下载到node_modules文件夹,并且开发依赖症会多一个element-ui

2.使用element-ui

2.1完整引入 Element-ui

在 main.js 中写入以下内容

import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引入element-ui样式文件
Vue.config.productionTip = false
Vue.use(ElementUI);
// 全局注入element-ui
new Vue({el: '#app',render: h => h(App),
}).$mount('#app')

在App.vue使用一下element的组件,我这里使用的是element-ui中的按钮组件

<template><div id="app"><!-- 引入element当中的按钮组件 --><el-button type="primary">主要按钮</el-button></div>
</template><script>
export default {name: 'App',}
</script><style></style>

效果

由上图可可见,引入并且使用成功

2.2按需引入 Element-ui

借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
安装 babel-plugin-component:

npm install babel-plugin-component -D


然后,将babelrc 修改为:

在我们cli脚手架有一个babel.config.js文件,这个文件其实和babelrc 是一样的作用,所以我们在babel.config.js进行以下配置。

module.exports = {presets: ['@vue/cli-plugin-babel/preset',["@babel/preset-env", { "modules": false }]],"plugins": [["component",{"libraryName": "element-ui","styleLibraryName": "theme-chalk"}]]
}

接下来,如果只引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:

import Vue from 'vue'
import App from './App.vue'
import 'element-ui/lib/theme-chalk/index.css';
// 引入element-ui样式文件
Vue.config.productionTip = false
import { Button } from 'element-ui';
Vue.use(Button)
// 按需引入
new Vue({el: '#app',render: h => h(App),
}).$mount('#app')

在App.vue使用一下element的组件,我这里使用的是element-ui中的按钮组件

<template><div id="app"><!-- 引入element当中的按钮组件 --><el-button type="primary">主要按钮</el-button></div>
</template><script>
export default {name: 'App',}
</script><style></style>

效果

完整引入和按需引入就是打包有一点区别,节省打包后dist文件夹体积
打包命令

npm run build

新手建议使用完整使用

扩展(element-ui文档)

在每个组件底部都有一个当前组件的属性和方法的介绍,我们也一定要学会查阅文档,在工作当中,也能提高我们的工作效率!
接下来就拿el-button组件做个介绍
根据需求,在我们组件添加相关的属性和方法即可

四、在vue中引入vue-router

没有基础的朋友可以看一下下面这篇文章对vue的讲解,路由引入配置等等,做个简单了解!也可以查看官方网站进行学习,链接在下方,可以看一看!
Vue从入门到精通(第三方插件使用+Axios封装+Vuex状态管理+Vue3新特性)
vue Router3.x主要配合vue2使用,我们这个项目看下面3.x的就可以
vue Router3.x官网
vue Router4.x官网主要配合vue3使用
vue Router4.x官网

1.npm安装vue-router

由于是vue2项目,我们需要安装vue Router3.x版本的
另外,如果想安装3.x版本最新,这里给大家推荐一个网站npm,在搜索栏搜索vue-router即可查看,以后的一些插件依赖也可以在这里搜查看最新版本
npm


通过查看最新的是3.6.5,我们在安装是后面@版本号即可

npm install vue-router@3.6.5

2.vue-router路由配置(上)

2.1vue-router路由配置代码

我们在src项目目录下新建一个router文件夹,然后新建一个index.js作为我们项目的路由配置文件

2.1.1.创建路由页面组件

我们在src项目目录下新建一个views文件夹,然后新建我们的路由页面组件

Home.vue页面组件

<template><div><h1>Home页面</h1></div>
</template>
<script>
export default{data() {return{}}
}
</script>

User.vue页面组件

<template><div><h1>User页面</h1></div>
</template>
<script>
export default{data() {return{}}
}
</script>
2.1.2 配置路由文件

写入以下代码:
页面组件在2.1.1创建

// 路由配置文件// 如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能,也可以再main.js引入
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 1.创建路由组件,也就是我们创建views里面的组件文件
// 引入我们创建的vue组件文件
import Home from '../views/Home.vue'
import User from '../views/User.vue'
// 2. 定义路由
// 将路由与组件进行映射
const routes = [{ path: '/Home', component: Home },{ path: '/User', component: User }
]
// 3. 创建 router 实例,然后传routes配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({routes // (缩写) 相当于 routes: routes
})// 导出我们的router实例
export default router

在main.js引入路由配置文件,然后挂载router

import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引入element-ui样式文件
import router from './router'
// 引入路由文件
Vue.config.productionTip = false
Vue.use(ElementUI);
// 全局注入element-uinew Vue({router,// 挂载router实例el: '#app',render: h => h(App),
}).$mount('#app')
2.1.3路由出口

路由出口=>路由匹配到的组件将渲染在这里
APP.vue

<template><div id="app"><!-- 路由出口 --><!-- 路由匹配到的组件将渲染在这里 --><router-view></router-view></div>
</template><script>
export default {name: 'App',data() {return{}}
}
</script><style></style>
扩展 eslint


eslint是一套规范,如果我们按照它所定义的规范来进行代码的编写,但是有时候会对我们的开发有一定影响的,那么下面解决一下这个问题。
在项目文件vue.config.js中加入一行 lintOnSave:false 关闭eslint校验

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,lintOnSave:false//关闭eslint校验
})


然后重新启动项目

这样就可以启动成功了!!!接下来我们就去看看路由的展示,根据我们配置路径然后匹配映射的组件文件中的页面


3.vue-router路由配置(下)

3.1vue-router中的嵌套路由


嵌套路由官方实例
/user/子路由进行匹配,实现动态路由
要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置:

3.1.1 项目子路由配置
主页面组件

Main.vue

<template><div><div> Main下面子路由的路由出口</div><!-- router-view Main下面子路由的路由出口 --><router-view></router-view></div>
</template>
<script>
export default{data(){return{}}
}
</script>

路由文件更改

// 路由配置文件// 如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能,也可以再main.js引入
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import Main from '../views/Mian.vue'
// 主页面
// 1.创建路由组件,也就是我们创建views里面的组件文件
// 引入我们创建的vue组件文件
import Home from '../views/Home.vue'
import User from '../views/User.vue'
// 2. 定义路由
// 将路由与组件进行映射
const routes = [// 主页面{path:'/',//  /默认是主出口主页面组件component:Main,children:[// 子路由{ path: 'Home', component: Home },{ path: 'User', component: User }]},]
// 3. 创建 router 实例,然后传routes配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({routes // (缩写) 相当于 routes: routes
})// 导出我们的router实例
export default router

效果
主页面

Home子路由匹配

User子路由匹配

五、vue通用管理后台(整体UI搭建)

在前面我们已经对element-ui和路由的使用有了一定的了解,那么接下来我们就要正式开始我们通用管理后台的布局和样式的搭建了!

1、通用管理后台(布局)

布局的话我们使用element-ui的Container 布局容器组件

Container 布局容器
用于布局的容器组件,方便快速搭建页面的基本结构:
:外层容器。当子元素中包含 或 时,全部子元素会垂直上下排列,否则会水平左右排列。
:顶栏容器。
:侧边栏容器。
:主要区域容器。
:底栏容器。
我们要用的布局效果图,

接下来就可以修改我们的主入口代码,把布局组件引入进去

1.1、使用 Container 布局容器进行布局

相关代码如下
main.vue

<template><div><el-container><el-aside width="200px">Aside</el-aside><el-container><el-header>Header</el-header><el-main>main        <!-- router-view Main下面子路由的路由出口 --><router-view></router-view></el-main></el-container></el-container></div>
</template>
<script>
export default {data() {}
}
</script>

效果

2、通用管理后台(完善左侧菜单栏)

布局的话我们使用element-ui的NavMenu导航菜单布局容器进行对el-aside菜单区域的内容修改

在里面找到根据我们项目的需求差不多的菜单栏

找到后cv到我们的项目中来

2.1、使用 NavMenu 导航菜单布局容器进行完善左侧菜单栏

因为菜单栏功能属于比较单一的,我们在工作中,对于单一的功能应该拆开成一个单一的组件,然后进行封装,这样便于后期的维护!
我们在components下新建一个CommonAside.vue用来放我们的菜单组件

相关代码
CommonAside.vue
封装菜单

<template>
<el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse"><el-submenu index="1"><template slot="title"><i class="el-icon-location"></i><span slot="title">导航一</span></template><el-menu-item-group><span slot="title">分组一</span><el-menu-item index="1-1">选项1</el-menu-item><el-menu-item index="1-2">选项2</el-menu-item></el-menu-item-group><el-menu-item-group title="分组2"><el-menu-item index="1-3">选项3</el-menu-item></el-menu-item-group><el-submenu index="1-4"><span slot="title">选项4</span><el-menu-item index="1-4-1">选项1</el-menu-item></el-submenu></el-submenu><el-menu-item index="2"><i class="el-icon-menu"></i><span slot="title">导航二</span></el-menu-item><el-menu-item index="3" disabled><i class="el-icon-document"></i><span slot="title">导航三</span></el-menu-item><el-menu-item index="4"><i class="el-icon-setting"></i><span slot="title">导航四</span></el-menu-item>
</el-menu>
</template><style>.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;}
</style><script>export default {data() {return {isCollapse: true};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);}}}
</script>

Main.vue
引入封装好的菜单组件并且使用

<template><div><el-container><el-aside width="200px"><!-- 把引入的CommonAside菜单组件进行使用 --><CommonAside/></el-aside><el-container><el-header>Header</el-header><el-main>main<!-- router-view Main下面子路由的路由出口 --><router-view></router-view></el-main></el-container></el-container></div>
</template>
<script>
import CommonAside from '../components/CommonAside.vue'
// 导入拆分的菜单组件
export default {// 在components中引入组件,引入好,就可以在template中直接使用了components: {CommonAside},data() {}
}
</script>

效果

3、通用管理后台(完善菜单)

我们菜单已经引入成功,接下来我们进一步的完善菜单当前我们的菜单默认是收起的状态,我们要修改为展开的,我们项目菜单一种是有子菜单(二级菜单),一种是没有子菜单(一级菜单),另外,还需要做的就是,当我们选中当前菜单时,当前菜单高亮显示

3.1 、修改菜单默认收起选项

改变el-menu组件的collapse,把值给false即可

相关代码
在封装CommonAside.vue组件找到collapse属性

<el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse">

在CommonAside.vue组件data找到这个属性值设置为false

    data() {return {isCollapse: false  //false展开,true收起};},

3.2、修改菜单的结构

根据需求,把不需要的菜单进行删除,整体结构修改为我们想要的效果
相关代码
CommonAside.vue

<template>
<el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse"><el-menu-item index="2"><i class="el-icon-menu"></i><span slot="title">导航一</span></el-menu-item><el-submenu index="1"><template slot="title"><i class="el-icon-location"></i><span slot="title">导航二</span></template><el-menu-item-group><el-menu-item index="1-1">选项1</el-menu-item></el-menu-item-group></el-submenu>
</el-menu>
</template>
<style>.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;}
</style><script>export default {data() {return {isCollapse: false  //false展开,true收起};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);}}}
</script>

效果
修改后的菜单

4、通用管理后台(根据菜单数据匹配相关页面)

根据我们点击的菜单,el-main展示相关的页面

4.1、menu菜单数据定义过滤

menu菜单数据
正常开发中,这些数据可能配合后端接口返回给我们,然后可以做权限判断,目前的话咱们就先写死!menuData就是我们的菜单数据

 data() {return {isCollapse: false,  //false展开,true收起// menuData 菜单数据menuData:[{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: 'setting',url: 'Other/PageTwo'}]}]};},

上列数据中有children的代表有二级菜单,没有的则只有一级菜单,所以我们要判断菜单数据有没有子菜单,可以根据children进行判断
使用计算属性过滤

    computed:{// computed计算属性// 对menuData菜单数据进行过滤,分组// 1.没有子菜单、menuNoChildren(){return this.menuData.filter(item=>!item.children)},// 2.有子菜单menuHasChildren(){return this.menuData.filter(item=>item.children)}}

我们在渲染数据之前,应该对数据进行一个分组(有子菜单menuHasChildren和无子菜单menuNoChildren),分组完成之后使用vue中的v-for指令遍历生成菜单

4.2、一级动态菜单生成

遍历我们过滤好的menuNoChildren无菜单数据,生成一级菜单
相关代码
CommonAside.vue

<template>
<el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse"><el-menu-item v-for="item in menuNoChildren"  :key="item.name" :index="item.name"><!-- 遍历生成无子菜单数据 --><i :class="`el-icon-${item.icon}`"></i><!-- i标签是渲染的element-ui的图标,使用了es6中的自符模板 --><span slot="title">{{ item.label }}</span><!-- span 标签是菜单名字 --></el-menu-item><el-submenu index="1"><template slot="title"><i class="el-icon-location"></i><span slot="title">导航二</span></template><el-menu-item-group><!-- 下方是二级菜单 --><el-menu-item index="1-1">选项1</el-menu-item></el-menu-item-group></el-submenu>
</el-menu>
</template>
<style>.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;}
</style><script>export default {data() {return {isCollapse: false,  //false展开,true收起// menuData 菜单数据menuData:[{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: 'setting',url: 'Other/PageTwo'}]}]};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);}},computed:{// computed计算属性// 对menuData菜单数据进行过滤,分组// 1.没有子菜单、menuNoChildren(){return this.menuData.filter(item=>!item.children)},// 2.有子菜单menuHasChildren(){return this.menuData.filter(item=>item.children)}}}
</script>

效果

4.3、二级动态菜单生成

遍历我们过滤好的menuHasChildren无菜单数据,生成二级菜单
==需要注意有二级菜单的子菜单遍历生成时不能使用和有二级菜单的一级菜单遍历相同名字 ==
相关代码
CommonAside.vue

<template><el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose":collapse="isCollapse"><el-menu-item v-for="item in menuNoChildren" :key="item.name" :index="item.name"><!-- 遍历生成无子菜单数据 --><i :class="`el-icon-${item.icon}`"></i><!-- i标签是渲染的element-ui的图标,使用了es6中的自符模板 --><span slot="title">{{ item.label }}</span><!-- span 标签是菜单名字 --></el-menu-item><!-- 遍历生成有二级菜单的一级菜单菜单 --><el-submenu v-for="item in menuHasChildren" :key="item.label" :index="item.label"><template slot="title"><i :class="`el-icon-${item.icon}`"></i><span slot="title">{{ item.label }}</span></template><!-- 遍历生成有二级菜单的子菜单 --><el-menu-item-group v-for="subItem in item.children" :key="subItem.path"><!-- 子菜单渲染 --><el-menu-item :index="subItem.path">{{ subItem.label }}</el-menu-item></el-menu-item-group></el-submenu></el-menu>
</template>
<style>
.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;
}
</style><script>
export default {data() {return {isCollapse: false,  //false展开,true收起// menuData 菜单数据menuData: [{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: 'setting',url: 'Other/PageTwo'}]}]};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);}},computed: {// computed计算属性// 对menuData菜单数据进行过滤,分组// 1.没有子菜单、menuNoChildren() {return this.menuData.filter(item => !item.children)},// 2.有子菜单menuHasChildren() {return this.menuData.filter(item => item.children)}}
}
</script>

效果

4.4、菜单样式与less引入

上面我们已经把菜单已经可以正常使用了,接下来我们对其样式进行完善一下,调整一下风格。
el-menu有以下三个属性

  • background-color=“#545c64”=>背景颜色
  • text-color=“#fff”=>字体颜色
  • active-text-color=“#ffd04b”=>选中后字体颜色


效果

修改整体高度和宽度

4.4.1、less使用和引入

less是css语法增强版的,可以使用less在我们这种模块化的项目中能够快速的创建我们的样式
less官方文档

下载less

npm i less@4.1.2
npm i less-loader@6.0.0  //less的解析器


使用less改写菜单样式

<template><el-menudefault-active="1-4-1"class="el-menu-vertical-demo"@open="handleOpen"@close="handleClose":collapse="isCollapse"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"><h3>vue前端通用管理后台</h3><el-menu-itemv-for="item in menuNoChildren":key="item.name":index="item.name"><!-- 遍历生成无子菜单数据 --><i :class="`el-icon-${item.icon}`"></i><!-- i标签是渲染的element-ui的图标,使用了es6中的自符模板 --><span slot="title">{{ item.label }}</span><!-- span 标签是菜单名字 --></el-menu-item><!-- 遍历生成有二级菜单的一级菜单菜单 --><el-submenuv-for="item in menuHasChildren":key="item.label":index="item.label"><template slot="title"><i :class="`el-icon-${item.icon}`"></i><span slot="title">{{ item.label }}</span></template><!-- 遍历生成有二级菜单的子菜单 --><el-menu-item-group v-for="subItem in item.children" :key="subItem.path"><!-- 子菜单渲染 --><el-menu-item :index="subItem.path">{{ subItem.label }}</el-menu-item></el-menu-item-group></el-submenu></el-menu>
</template>
<style scoped lang="less">
.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;
}
// 菜单高度和标题样式
.el-menu{height: 100vh;h3{margin: 0;color: white;text-align: center;line-height: 48px;font-size: 16px;font-weight: 400;}
}
</style><script>
export default {data() {return {isCollapse: false, //false展开,true收起// menuData 菜单数据menuData: [{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: "setting",url: "Other/PageTwo",},],},],};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);},},computed: {// computed计算属性// 对menuData菜单数据进行过滤,分组// 1.没有子菜单、menuNoChildren() {return this.menuData.filter((item) => !item.children);},// 2.有子菜单menuHasChildren() {return this.menuData.filter((item) => item.children);},},
};
</script>

效果

清除标签默认样式样式
APP.vue文件

<template><div id="app"><!-- 路由出口 --><!-- 路由匹配到的组件将渲染在这里 --><router-view></router-view></div>
</template><script>
export default {name: 'App',}
</script><style lang="less">
// 清除html,body的内网边距
html,body{margin: 0;padding: 0;
}
</style>

效果

4.5、菜单点击跳转功能实现

我们上面已经完成了左侧的样式以及基本的布局,现在我们要完善一下当我们点击菜单选项,完成路由跳转展示

4.5.1、根据菜单数据,我们先定义一下我们未定义的路由**

先新建对应路由页面文件
Mall.vue商品管理页面

<template><div><h1>Mall页面</h1></div>
</template>
<script>
export default{data(){return{}}
}
</script>

qtPageOne.vue其他页面1页面

<template><div><h1>qtPageOne页面</h1></div>
</template>
<script>
export default{data(){return{}}
}
</script>

qtPageTow.vue其他页面2页面

<template><div><h1>qtPageTow页面</h1></div>
</template>
<script>
export default{data(){return{}}
}
</script>

router文件夹index.js路由·配置文件

// 路由配置文件// 如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能,也可以再main.js引入
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
import Main from "../views/Mian.vue";
// 主页面
// 1.创建路由组件,也就是我们创建views里面的组件文件
// 引入我们创建的vue组件文件
import Home from "../views/Home.vue";
import User from "../views/User.vue";
import Mall from "../views/Mall.vue";
import qtPageOne from "../views/qtPageOne.vue";
import qtPageTow from "../views/qtPageTow.vue";
// 2. 定义路由
// 将路由与组件进行映射
const routes = [// 主页面{path: "/",//  /默认是主出口主页面组件redirect:'home',// 重定向到homecomponent: Main,children: [// 子路由{ path: "home", component: Home },// 首页{ path: "user", component: User },// 用户管理{ path: "mall", component: Mall },// 商品管理{ path: "page1", component: qtPageOne },// 其他页面1{ path: "page2", component: qtPageTow },// 其他页面2],},
];
// 3. 创建 router 实例,然后传routes配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({routes, // (缩写) 相当于 routes: routes
});// 导出我们的router实例
export default router;

文件结构

4.5.2、点击菜单,根据点击菜单的数据跳转应路由页面**
<template><el-menudefault-active="1-4-1"class="el-menu-vertical-demo"@open="handleOpen"@close="handleClose":collapse="isCollapse"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"><h3>vue前端通用管理后台</h3><!-- 点击菜单选项触发clickMenu事件 --><el-menu-item@click="clickMenu(item)"v-for="item in menuNoChildren":key="item.name":index="item.name"><!-- 遍历生成无子菜单数据 --><i :class="`el-icon-${item.icon}`"></i><!-- i标签是渲染的element-ui的图标,使用了es6中的自符模板 --><span slot="title">{{ item.label }}</span><!-- span 标签是菜单名字 --></el-menu-item><!-- 遍历生成有二级菜单的一级菜单菜单 --><el-submenuv-for="item in menuHasChildren":key="item.label":index="item.label"><template slot="title"><i :class="`el-icon-${item.icon}`"></i><span slot="title">{{ item.label }}</span></template><!-- 遍历生成有二级菜单的子菜单 --><el-menu-item-group v-for="subItem in item.children" :key="subItem.path"><!-- 子菜单渲染 --><!-- clickMenu跳转 --><el-menu-item     @click="clickMenu(subItem)" :index="subItem.path">{{ subItem.label }}</el-menu-item></el-menu-item-group></el-submenu></el-menu>
</template>
<style scoped lang="less">
.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;
}
// 菜单高度和标题样式
.el-menu {height: 100vh;border-right: none;h3 {margin: 0;color: white;text-align: center;line-height: 48px;font-size: 16px;font-weight: 400;}
}
</style><script>
export default {data() {return {isCollapse: false, //false展开,true收起// menuData 菜单数据menuData: [{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: "setting",url: "Other/PageTwo",},],},],};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);},// 点击菜单,跳转clickMenu(item) {// item 触发传递的数据console.log(item.path);// 添加容错逻辑// 当我们的路由与我们跳转的路由不一致时,才允许跳转// 当前页面路由if(this.$route.path!=item.path &&!(this.$route.path==='/home'&&(item.path==='/'))){this.$router.push(item.path);}},},computed: {// computed计算属性// 对menuData菜单数据进行过滤,分组// 1.没有子菜单、menuNoChildren() {return this.menuData.filter((item) => !item.children);},// 2.有子菜单menuHasChildren() {return this.menuData.filter((item) => item.children);},},
};
</script>

效果

5、通用管理后台(header组件与样式搭建)

5.1、封装header区域组件与样式

菜单区域我们已经完成了,接下来我们完成一下header区域的布局和样式编写,一个是左侧的按钮区域和面包屑区域,另外就是用户头像区域带下来菜单。
封装创建一个CommonHeader.vue

<template><div class="header-content"><!-- 头部组件 --><div class="l-content"><!-- 左侧 --><!-- 点icon的按钮 --><el-button icon="el-icon-menu" size="mini"></el-button><!-- 面包屑,先用span标签代替 --><span class="text">首页</span></div><div class="r-content"><!-- 右侧 --><!-- el-dropdown 下拉菜单--><el-dropdown><img class="user" src="../assets/tx.png" alt=""><el-dropdown-menu slot="dropdown"><el-dropdown-item>个人中心</el-dropdown-item><el-dropdown-item>退出</el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template>
<script>
export default {data() {return {};}
};
</script>
<style lang="less" scoped>
.header-content {padding: 0 20px;background-color: #333;height: 60px;display: flex;justify-content: space-between;align-items: center;.text {color: white;font-size: 14px;margin-left: 10px;}.r-content{.user{// 头像width: 40px;height: 40px;border-radius: 50%;}}
}
</style>


在Main.vue中引入并且使用
Main.vue

<template><div><el-container><el-aside width="200px"><!-- 把引入的CommonAside菜单组件进行使用 --><CommonAside/></el-aside><el-container><el-header><CommonHeader/><!-- 把引入的CommonHeader头部组件进行使用 --></el-header><el-main><!-- router-view Main下面子路由的路由出口 --><router-view></router-view></el-main></el-container></el-container></div>
</template>
<script>
import CommonAside from '../components/CommonAside.vue'
// 导入拆分的菜单组件
import CommonHeader from '../components/CommonHeader.vue'
// 导入拆分的头部组件
export default {// 在components中引入组件,引入好,就可以在template中直接使用了components: {CommonAside,CommonHeader},data() {return{}}
}
</script>
<style>
.el-header{padding: 0;border: none;
}
</style>

效果

5.2、通过vuex实现左侧折叠

前面已经完善了header区域的布局和样式,接下来把header区域按钮的点击操作菜单折叠与展开完善一下!但是按钮在CommonHeader.vue组件中,那么如何去操作CommonAside.vue菜单组件呢?


因为状态修改牵扯到俩个组件中的变化,接下来我们就用vuex去实现

5.2.1、什么是vuex?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex分为俩个比较大的版本,一个是4.0(主要针对于vue3),一个3.0(主要针对vue2),
Vuex4官网
Vuex3官网

5.2.2、安装vuex
npm i vuex@3.6.2

5.2.3、配置vuex

在src文件下创建一个store文件夹,在store新建一个index.js文件配置vuex
因为菜单和用户是俩部分单独的一块信息,对于这种数据,按照模块化的思想,把它拆分成俩个 modules
新建一个tab.js用于管理菜单的数据

// 管理菜单的数据
export default{state:{isCollapse:false,// isCollapse用于控制菜单的展开和收起},mutations:{// 修改菜单展开收起方法collapseMenu(state){state.isCollapse=!state.isCollapse}}
}``
在store下面的index.js配置vuex和注入`
```js
// 引入vue
import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'
// 引入管理菜单的数据
Vue.use(Vuex)
// 将Vuex全局注入// 创建vuex实例
export default new Vuex.Store({modules:{tab}
})

在mian.js将我们的vuex实例挂载到vue上

import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引入element-ui样式文件
import router from './router'
// 引入路由文件
import store from'./store'
Vue.config.productionTip = false
Vue.use(ElementUI);
// 全局注入element-uinew Vue({router,// 挂载router实例store,// 挂载vuex实例el: '#app',render: h => h(App),
}).$mount('#app')
5.2.4、使用vuex

在我们组件中使用vuex完成我们的点击按钮让菜单展开收起的功能
在CommonHeader.vue中点击按钮调用mutations方法修改vuex中state里isCollapse数据
CommonHeader.vue
HandelMenu方法使我们点击按钮事件方法

<template><div class="header-content"><!-- 头部组件 --><div class="l-content"><!-- 左侧 --><!-- icon的按钮 ,HandelMenu点击按钮方法 --><el-button @click="HandelMenu" icon="el-icon-menu" size="mini"></el-button><!-- 面包屑,先用span标签代替 --><span class="text">首页</span></div><div class="r-content"><!-- 右侧 --><!-- el-dropdown 下拉菜单--><el-dropdown><img class="user" src="../assets/tx.png" alt=""><el-dropdown-menu slot="dropdown"><el-dropdown-item>个人中心</el-dropdown-item><el-dropdown-item>退出</el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template>
<script>
export default {data() {return {};},methods:{HandelMenu(){// 按钮点击事件,调用vuex的mutations方法this.$store.commit('collapseMenu')}}
};
</script>
<style lang="less" scoped>
.header-content {padding: 0 20px;background-color: #333;height: 60px;display: flex;justify-content: space-between;align-items: center;.text {color: white;font-size: 14px;margin-left: 10px;}.r-content{.user{// 头像width: 40px;height: 40px;border-radius: 50%;}}
}
</style>

CommonAside.vue
获取vuex的state中的isCollapse数据,绑定我们菜单实现菜单展开和收缩
在computed属性中进行使用的

<template><el-menudefault-active="1-4-1"class="el-menu-vertical-demo"@open="handleOpen"@close="handleClose":collapse="isCollapse"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"><h3>vue前端通用管理后台</h3><!-- 点击菜单选项触发clickMenu事件 --><el-menu-item@click="clickMenu(item)"v-for="item in menuNoChildren":key="item.name":index="item.name"><!-- 遍历生成无子菜单数据 --><i :class="`el-icon-${item.icon}`"></i><!-- i标签是渲染的element-ui的图标,使用了es6中的自符模板 --><span slot="title">{{ item.label }}</span><!-- span 标签是菜单名字 --></el-menu-item><!-- 遍历生成有二级菜单的一级菜单菜单 --><el-submenuv-for="item in menuHasChildren":key="item.label":index="item.label"><template slot="title"><i :class="`el-icon-${item.icon}`"></i><span slot="title">{{ item.label }}</span></template><!-- 遍历生成有二级菜单的子菜单 --><el-menu-item-group v-for="subItem in item.children" :key="subItem.path"><!-- 子菜单渲染 --><!-- clickMenu跳转 --><el-menu-item     @click="clickMenu(subItem)" :index="subItem.path">{{ subItem.label }}</el-menu-item></el-menu-item-group></el-submenu></el-menu>
</template>
<style scoped lang="less">
.el-menu-vertical-demo:not(.el-menu--collapse) {width: 200px;min-height: 400px;
}
// 菜单高度和标题样式
.el-menu {height: 100vh;border-right: none;h3 {margin: 0;color: white;text-align: center;line-height: 48px;font-size: 16px;font-weight: 400;}
}
</style><script>
export default {data() {return {// menuData 菜单数据menuData: [{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: "setting",url: "Other/PageTwo",},],},],};},methods: {handleOpen(key, keyPath) {console.log(key, keyPath);},handleClose(key, keyPath) {console.log(key, keyPath);},// 点击菜单,跳转clickMenu(item) {// item 触发传递的数据console.log(item.path);// 添加容错逻辑// 当我们的路由与我们跳转的路由不一致时,才允许跳转// 当前页面路由if(this.$route.path!=item.path &&!(this.$route.path==='/home'&&(item.path==='/'))){this.$router.push(item.path);}},},computed: {// computed计算属性// 对menuData菜单数据进行过滤,分组// 1.没有子菜单、menuNoChildren() {return this.menuData.filter((item) => !item.children);},// 2.有子菜单menuHasChildren() {return this.menuData.filter((item) => item.children);},// 在computed属性下isCollapse(){return this.$store.state.tab.isCollapse// 我们state的isCollapse数据//false展开,true收起}},
};
</script>

效果
展开

收缩=>可以看到有bug

5.3、解决菜单收缩展开的bug

利用三元判断修改左侧收起标题问题

    <h3>{{isCollapse?'vue前端通用管理后台':'后台'}}</h3>

宽度问题
Main.vue中el-aside 将之前我们设置的width="200px"值改为auto自适应

      <el-aside width="auto"><!-- 把引入的CommonAside菜单组件进行使用 --><CommonAside/></el-aside>

效果

5、通用管理后台(home组件与样式搭建)

关于home组件包含左侧用户信息和table数据,右侧是订单数据加上折线图柱状图饼状图,并且自适应布局(使用element-ui中的layout布局),鼠标移入区域有阴影(element-ui中的card组件)

5.1、home组件布局

5.1.1、home组件–左侧用户信息

Home.vue

<template><el-row><!-- span是所占的比例 --><el-col :span="8"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt=""><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card></el-col><el-col :span="16"></el-col></el-row>
</template>
<script>
export default {data() {return {};}
};
</script>
<style lang="less" scoped>
.user{display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img{margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail{// 用户详情信息.name{// 用户名font-size: 32px;margin-bottom: 10px;}.access{// 角色color: #999;}}
}.user-info{// 登录时间和地点
p{line-height: 28px;font-size: 14px;color: #999;span{color: #666;margin-left: 60px;}
}
}
</style>

效果

5.1.2、home组件–左侧table表格信息

对于table在后台管理中是一个用的比较多点,基本每个后台都有,像列表页信息的展示等等,我们使用element-ui组件库中的el-table实现
表格数据

      tableData: [{name: "HTML",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "CSS",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "JavaScript",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "Vue",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "React",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "微信小程序",todayBuy: 100,monthBuy: 300,totalBuy: 800}],

Home.vue表格=>(静态)

<template><el-row><!-- span是所占的比例 --><el-col :span="8"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table></el-card></el-col><el-col :span="16"></el-col></el-row>
</template>
<script>
export default {data() {return {// tableData表格数据tableData: [{name: "HTML",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "CSS",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "JavaScript",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "Vue",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "React",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "微信小程序",todayBuy: 100,monthBuy: 300,totalBuy: 800}],};}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
</style>

Home.vue表格=>(动态)

<template><el-row><!-- span是所占的比例 --><el-col :span="8"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table> --><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16"></el-col></el-row>
</template>
<script>
export default {data() {return {// tableData表格数据tableData: [{name: "HTML",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "CSS",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "JavaScript",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "Vue",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "React",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "微信小程序",todayBuy: 100,monthBuy: 300,totalBuy: 800}],// tableLabel根据表格整理表格表格label数据tableLabel: {name: '技术栈',todayBuy: '今日学习人数',monthBuy: '本月学习人数',totalBuy: '学习总数据'},};}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
</style>

效果

5.1.3、home组件–右侧学习数据统计实现

前面我们已经完成了左侧布局和table数据展示,图表使用了es6模板字符串拼接类名结合element的图表实现的
Home.vue=>class="num"盒子

<template><el-row><!-- span是所占的比例 --><el-col :span="8"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table>--><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16"><!-- 右侧区域 --><div class="num"><el-card :body-style="{display:'flex',padding:0}" v-for="item in countData" :key="item.name"><i :style="{background:item.color}" class="icon" :class="`el-icon-${item.icon}`"></i><div class="detail"><p class="dataPrice">{{ item.value }}</p><p class="dataDesc">{{ item.name }}</p></div></el-card></div></el-col></el-row>
</template>
<script>
export default {data() {return {// tableData表格数据tableData: [{name: "HTML",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "CSS",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "JavaScript",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "Vue",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "React",todayBuy: 100,monthBuy: 300,totalBuy: 800},{name: "微信小程序",todayBuy: 100,monthBuy: 300,totalBuy: 800}],// tableLabel根据表格整理表格表格label数据tableLabel: {name: "技术栈",todayBuy: "今日学习人数",monthBuy: "本月学习人数",totalBuy: "学习总数据"},// countData 学习数据统计countData: [{name: "今日学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "今日收藏文章",value: 210,icon: "star-on",color: "red"},{name: "今日查看数据",value: 1234,icon: "thumb",color: "chartreuse"},{name: "本月学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "本月收藏文章",value: 210,icon: "star-on",color: "red"},{name: "本月查看数据",value: 1234,icon: "thumb",color: "chartreuse"}]};}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
// 右侧学习数据
.num {display: flex;flex-wrap: wrap;  // 强制换行justify-content: space-between;.icon {// 图表width: 80px;height: 80px;font-size: 30px;text-align: center;line-height: 80px;color: white;}.detail{display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.dataPrice{font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.dataDesc{color: #999;font-size: 14px;text-align: center;}}.el-card{// 右侧卡片width: 32%;margin-bottom: 20px;}}
</style>

效果

6、通用管理后台(Axios 和Mock.js模拟数据)

那么接下来我们继续完成剩下的图表展示部分,我们除了图表的展示,另外呢就是图表的数据,不过在这个到目前为止,我们的数据还都是写死的,那么在我们实际项目开发中,我们的数据一般是由后端返回给我们的,后端经过逻辑处理然后返回给我们
Axios 文档

Mock.js文档

6.1、扩展–前后端联调

在我们开发的页面中,我们的代码都是运行在游览器上的,那我们想获取数据,具体步骤是游览器向服务器发送一个ajax请求,服务器接收到请求之后之后,将数据回传给游览器
发送请求有很多种方式,比如原生js的XMLHttpRequest对象实现ajax请求,当然这种实现方式是比较复杂的,但是在项目开发中,我们更多的是使用工具或者第三方库来完成我们这些功能,接下来就介绍一个非常使用的axios插件

6.2、Axios

Axios 相对于我们原生js的XMLHttpRequest,Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。从写法上可以避免回调地狱的问题,因为我们可以通async和await这俩种写法结合我们promise 将整个异步的过程转换成同步的写法,另外Axios 封装了一些比较好用的api,我们可以直接调用它,实现我们一些一些接口请求的封装、公共的请求头等等

6.2.1、Axios 的基本使用

安装:

npm install axios

在项目文件下进行安装

6.2.2、Axios 的二次封装

安装完成之后,我们要使用axios做接口请求,在我们项目中接口是非常多的,我们要对这些进行一个二次封装,我们封装的就是 Axios 的实例,axios.create这个方法会返回一个实例,我们拿到这些实例之后,就可以调用这些实例的方法,这些实例的方法就具备我们创建Axios时配置的一些属性,对于这些实例我们还可以配置个拦截器(请求拦截器和响应拦截器) ,请求拦截器在发送请求之前进行的一些处理(像header请求头的内容token等等),响应拦截器是服务器端回传消息给我们的时候,在我们拿到数据之前可以做哪些处理 (像http的状态码,像非200的状态码可以给用户一些提示或者对业务数据进行逻辑判断等等,), 把相对繁琐的的一些请求进行一下处理。

接下来我们就完成Axios 在项目中的一个封装
在src文件夹下面新建一个utils文件夹用于做数据处理文件的文件夹
utils文件夹=>新建一个requerst.js文件
这里对拦截器没有过多地封装,大家可以根据自己需要网上找一下即可

import axios from 'axios'
// 引入axios
const http=axios.create({baseURL:'/api',// baseURL通用请求地址(地址前缀),这个地址根据实际项目中更改成自己的即可timeout:10000,// timeout 请求超时时间 单位是毫秒// 其余配置如果需要可参考axios文档进行配置=>http://www.axios-js.com/zh-cn/docs/vue-axios.html
})
// 拦截器
// 添加请求拦截器
http.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
http.interceptors.response.use(function (response) {// 对响应数据做点什么return response;
}, function (error) {// 对响应错误做点什么return Promise.reject(error);
});export default http
6.2.3、api调用接口文件封装

在src文件夹下面新建一个api文件夹用于放封装的接口调用文件
api文件夹=>新建index.js
因为涉及接口比较少,就以index.js做演示,后续大家可以根据项目需要进行一下分类单独封装

import http from "@/utils/request";
// 引入我们二次封装的axios实例// 接口调用封装
// 请求首页数据
export const getData=()=>{// /home/getData接口地址// 返回一个promise对象return http.get('/home/getData')
}

在页面中使用封装封装好的请求接口方法

<script>
import {getData} from '../api'
// 引入封装好的请求方法
export default {data() {return {},mounted(){// mounted挂载getData().then(res=>{//使用getData封装方法console.log(res)})}
};
</script>

结果
可以看到发送了请求,目前只是我们还没有后端服务,所以会报404的错误,不过从这里我们已经完成了Axios的二次封装,并且发送了请求

因为上面没有后端服务,那么接下来就简单介绍一下mock数据,模拟后端接口,实现交互

6.3、Mock.js

在没有后端提供数据的情况下,前端人员在自己写demo或者练手项目的时候可以使用mock.js来模拟数据
Mock是一个前端模拟后端接口的一个工具,我们可以通过拦截前端发起的请求,可以自己定义一些返回的数据,这种情况下是我们在不依赖后端的情况下,实现数据交互

6.3.1、Mock.js安装
# 安装
npm install mockjs

在项目文件下进行安装

6.3.2、Mock.mock()方法

Mock.mock()方法参数以及使用
Mock.mock( rurl?, rtype?, template|function( options ) )

6.3.3、Mock使用

api文件夹=>新建mock.js

import Mock from 'mockjs'
// 引入mock// 定义mock接口请求拦截
/*** Mock.mock参数* 第一个是拦截接口的地址* 第二个是接口请求类型(可省略)* 第三个是一个函数=>拦截后的请求逻辑*/
Mock.mock('/api/home/getData','get',function(){// 拦截后的请求逻辑console.log('拦截到了')
})

在main.js引入mock处理文件

import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引入element-ui样式文件
import router from './router'
// 引入路由文件
import store from'./store'// 引入mock处理文件
import './api/mock'
Vue.config.productionTip = false
Vue.use(ElementUI);
// 全局注入element-uinew Vue({router,// 挂载router实例store,// 挂载vuex实例el: '#app',render: h => h(App),
}).$mount('#app')

效果


以上是完成了第一步,对mock进行简单的使用,接下来就正式模拟接口和数据

6.3.4、Mock模拟接口数据

api文件夹=>新建mockServeData文件夹
用于存放对应页面模拟数据文件
mockServeData文件夹=>新建home.js
存放首页mock数据

// mock数据模拟
import Mock from 'mockjs'// 图表数据
let List = []
export default {getStatisticalData: () => {//Mock.Random.float 产生随机数100到8000之间 保留小数 最小0位 最大0位for (let i = 0; i < 7; i++) {List.push(Mock.mock({苹果: Mock.Random.float(100, 8000, 0, 0),vivo: Mock.Random.float(100, 8000, 0, 0),oppo: Mock.Random.float(100, 8000, 0, 0),魅族: Mock.Random.float(100, 8000, 0, 0),三星: Mock.Random.float(100, 8000, 0, 0),小米: Mock.Random.float(100, 8000, 0, 0)}))}return {code: 20000,data: {// 饼图videoData: [{name: '小米',value: 2999},{name: '苹果',value: 5999},{name: 'vivo',value: 1500},{name: 'oppo',value: 1999},{name: '魅族',value: 2200},{name: '三星',value: 4500}],// 柱状图userData: [{date: '周一',new: 5,active: 200},{date: '周二',new: 10,active: 500},{date: '周三',new: 12,active: 550},{date: '周四',new: 60,active: 800},{date: '周五',new: 65,active: 550},{date: '周六',new: 53,active: 770},{date: '周日',new: 33,active: 170}],// 折线图orderData: {date: ['20191001', '20191002', '20191003', '20191004', '20191005', '20191006', '20191007'],data: List},tableData: [{name: 'oppo',todayBuy: 500,monthBuy: 3500,totalBuy: 22000},{name: 'vivo',todayBuy: 300,monthBuy: 2200,totalBuy: 24000},{name: '苹果',todayBuy: 800,monthBuy: 4500,totalBuy: 65000},{name: '小米',todayBuy: 1200,monthBuy: 6500,totalBuy: 45000},{name: '三星',todayBuy: 300,monthBuy: 2000,totalBuy: 34000},{name: '魅族',todayBuy: 350,monthBuy: 3000,totalBuy: 22000}]}}}
}

将mock里面的第三个参数拦截后的请求逻辑进行一下替换


import Mock from 'mockjs'
// 引入mock
import homeApiData from './mockServeData/home'
//引入我们处理好的数据,作为进行拦截后的请求逻辑的数据
// 定义mock接口请求拦截
/*** Mock.mock参数* 第一个是拦截接口的地址* 第二个是接口请求类型(可省略)* 第三个是一个函数=>拦截后的请求逻辑*/
Mock.mock('/api/home/getData','get',homeApiData.getStatisticalData)

效果

7、通用管理后台(首页可视化图表样式调整)

在前面我们已经模拟了后端以及axios封装和接口的封装和调用,调用后也获取到了数据

接下来我们就把之前的数据全部换成我们的动态数据!

7.1、首页table由静态数据切换成动态数据**

Home.vue
将获取res.data.data.tableData是返回的table数据给tableData重新赋值,这样表格就变成了动态数据

<template><el-row><!-- span是所占的比例 --><el-col :span="8"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table>--><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16"><!-- 右侧区域 --><div class="num"><el-card :body-style="{display:'flex',padding:0}" v-for="item in countData" :key="item.name"><i :style="{background:item.color}" class="icon" :class="`el-icon-${item.icon}`"></i><div class="detail"><p class="dataPrice">{{ item.value }}</p><p class="dataDesc">{{ item.name }}</p></div></el-card></div></el-col></el-row>
</template>
<script>
import {getData} from '../api'
// 引入封装好的请求方法
export default {data() {return {// tableData表格数据tableData: [],// tableLabel根据表格整理表格表格label数据tableLabel: {name: "技术栈",todayBuy: "今日学习人数",monthBuy: "本月学习人数",totalBuy: "学习总数据"},// countData 学习数据统计countData: [{name: "今日学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "今日收藏文章",value: 210,icon: "star-on",color: "red"},{name: "今日查看数据",value: 1234,icon: "thumb",color: "chartreuse"},{name: "本月学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "本月收藏文章",value: 210,icon: "star-on",color: "red"},{name: "本月查看数据",value: 1234,icon: "thumb",color: "chartreuse"}]};},mounted(){// mounted挂载getData().then(res=>{// 获取res.data.data.tableData是返回的table数据this.tableData=res.data.data.tableDataconsole.log(this.tableData)})}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
// 右侧学习数据
.num {display: flex;flex-wrap: wrap;  // 强制换行justify-content: space-between;.icon {// 图表width: 80px;height: 80px;font-size: 30px;text-align: center;line-height: 80px;color: white;}.detail{display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.dataPrice{font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.dataDesc{color: #999;font-size: 14px;text-align: center;}}.el-card{// 右侧卡片width: 32%;margin-bottom: 20px;}}
</style>

效果

7.2、样式完善和图标表区域布局

左侧区域和右侧区域el-col加上一个padding值

 <el-col :span="8" style="padding-right:10px">//左侧的col<el-col :span="16"  style="padding-left:10px">//右侧的col

图表区域布局

<template><el-row><!-- span是所占的比例 --><el-col :span="8" style="padding-right:10px"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table>--><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16"  style="padding-left:10px"><!-- 右侧区域 --><div class="num"><el-card :body-style="{display:'flex',padding:0}" v-for="item in countData" :key="item.name"><i :style="{background:item.color}" class="icon" :class="`el-icon-${item.icon}`"></i><div class="detail"><p class="dataPrice">{{ item.value }}</p><p class="dataDesc">{{ item.name }}</p></div></el-card></div><el-card style="height:280px"><!-- 折线图 --></el-card><div class="tbStyle"><el-card  style="height:260px;width:48%;"><!-- 柱状图 --></el-card><el-card  style="height:260px;width:48%;"><!-- 饼图 --></el-card></div></el-col></el-row>
</template>
<script>
import {getData} from '../api'
// 引入封装好的请求方法
export default {data() {return {// tableData表格数据tableData: [],// tableLabel根据表格整理表格表格label数据tableLabel: {name: "技术栈",todayBuy: "今日学习人数",monthBuy: "本月学习人数",totalBuy: "学习总数据"},// countData 学习数据统计countData: [{name: "今日学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "今日收藏文章",value: 210,icon: "star-on",color: "red"},{name: "今日查看数据",value: 1234,icon: "thumb",color: "chartreuse"},{name: "本月学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "本月收藏文章",value: 210,icon: "star-on",color: "red"},{name: "本月查看数据",value: 1234,icon: "thumb",color: "chartreuse"}]};},mounted(){// mounted挂载getData().then(res=>{// 获取res.data.data.tableData是返回的table数据this.tableData=res.data.data.tableDataconsole.log(this.tableData)})}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
// 右侧学习数据
.num {display: flex;flex-wrap: wrap;  // 强制换行justify-content: space-between;.icon {// 图表width: 80px;height: 80px;font-size: 30px;text-align: center;line-height: 80px;color: white;}.detail{display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.dataPrice{font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.dataDesc{color: #999;font-size: 14px;text-align: center;}}.el-card{// 右侧卡片width: 32%;margin-bottom: 20px;}}
.tbStyle{// 底部图表容器样式margin-top: 20px;display: flex;justify-content: space-between;
}
</style>

图表区域效果

7.2.1、Echarts

Echarts图表官网

安装

npm i echarts@5.1.2

在项目文件夹下进行安装

安装完成后,接下来就在项目中使用把

7.2.1、折线图部分完善

上面已经对表格区域进行了一些简单的布局,接下来就是对空白区域进行一下图表的补充
Home.vue

<template><el-row><!-- span是所占的比例 --><el-col :span="8" style="padding-right:10px"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table>--><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16" style="padding-left:10px"><!-- 右侧区域 --><div class="num"><el-card:body-style="{display:'flex',padding:0}"v-for="item in countData":key="item.name"><i :style="{background:item.color}" class="icon" :class="`el-icon-${item.icon}`"></i><div class="detail"><p class="dataPrice">{{ item.value }}</p><p class="dataDesc">{{ item.name }}</p></div></el-card></div><el-card style="height:280px"><!-- 折线图 --><div ref="echartszx" style="height:280px"></div></el-card><div class="tbStyle"><el-card style="height:260px;width:48%;"><!-- 柱状图 --></el-card><el-card style="height:260px;width:48%;"><!-- 饼图 --></el-card></div></el-col></el-row>
</template>
<script>
import { getData } from "../api";
// 引入封装好的请求方法
import * as echarts from "echarts";
// 引入echarts
export default {data() {return {// tableData表格数据tableData: [],// orderData折线图数据orderData: [],// tableLabel根据表格整理表格表格label数据tableLabel: {name: "技术栈",todayBuy: "今日学习人数",monthBuy: "本月学习人数",totalBuy: "学习总数据"},// countData 学习数据统计countData: [{name: "今日学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "今日收藏文章",value: 210,icon: "star-on",color: "red"},{name: "今日查看数据",value: 1234,icon: "thumb",color: "chartreuse"},{name: "本月学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "本月收藏文章",value: 210,icon: "star-on",color: "red"},{name: "本月查看数据",value: 1234,icon: "thumb",color: "chartreuse"}]};},mounted() {// mounted挂载getData().then(res => {// 获取res.data.data.tableData是返回的table数据this.tableData = res.data.data.tableData;// 图表数据console.log(this.tableData);this.orderData = res.data.data.orderData;// orderData折线图数据// 基于准备好的dom,初始化echarts实例// 获取dom元素,然后初始化echartsconst echartszx = echarts.init(this.$refs.echartszx);// 指定图表的配置和数据let zxOption = {};// 处理xAxis  x轴数据const xAxis = Object.keys(this.orderData.data[0]);const xAxisData = {data: xAxis};// x轴zxOption.xAxis = xAxisData;// y轴zxOption.yAxis = {};// 图例zxOption.legend = xAxisData;// y轴对应数据zxOption.series = [];xAxis.forEach(key => {zxOption.series.push({name: key,data: this.orderData.data.map(item => item[key]),type: "line"});});console.log(zxOption);// 使用刚指定的配置项和数据显示图表。echartszx.setOption(zxOption);});}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
// 右侧学习数据
.num {display: flex;flex-wrap: wrap; // 强制换行justify-content: space-between;.icon {// 图表width: 80px;height: 80px;font-size: 30px;text-align: center;line-height: 80px;color: white;}.detail {display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.dataPrice {font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.dataDesc {color: #999;font-size: 14px;text-align: center;}}.el-card {// 右侧卡片width: 32%;margin-bottom: 20px;}
}
.tbStyle {// 底部图表容器样式margin-top: 20px;display: flex;justify-content: space-between;
}
</style>

效果

7.2.2、柱状图部分完善

接下来完成我们主页左下角柱状图部分
Home.vue

<template><el-row><!-- span是所占的比例 --><el-col :span="8" style="padding-right:10px"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table>--><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16" style="padding-left:10px"><!-- 右侧区域 --><div class="num"><el-card:body-style="{display:'flex',padding:0}"v-for="item in countData":key="item.name"><i :style="{background:item.color}" class="icon" :class="`el-icon-${item.icon}`"></i><div class="detail"><p class="dataPrice">{{ item.value }}</p><p class="dataDesc">{{ item.name }}</p></div></el-card></div><el-card style="height:280px"><!-- 折线图 --><div ref="echartszx" style="height:280px"></div></el-card><div class="tbStyle"><el-card style="height:260px;width:48%;"><!-- 柱状图 --><div ref="echartszzt" style="height:260px;"></div></el-card><el-card style="height:260px;width:48%;"><!-- 饼图 --></el-card></div></el-col></el-row>
</template>
<script>
import { getData } from "../api";
// 引入封装好的请求方法
import * as echarts from "echarts";
// 引入echarts
export default {data() {return {// tableData表格数据tableData: [],// orderData折线图数据orderData: [],// userData用户数据userData:[],// tableLabel根据表格整理表格表格label数据tableLabel: {name: "技术栈",todayBuy: "今日学习人数",monthBuy: "本月学习人数",totalBuy: "学习总数据"},// countData 学习数据统计countData: [{name: "今日学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "今日收藏文章",value: 210,icon: "star-on",color: "red"},{name: "今日查看数据",value: 1234,icon: "thumb",color: "chartreuse"},{name: "本月学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "本月收藏文章",value: 210,icon: "star-on",color: "red"},{name: "本月查看数据",value: 1234,icon: "thumb",color: "chartreuse"}]};},mounted() {// mounted挂载getData().then(res => {// 获取res.data.data.tableData是返回的table数据this.tableData = res.data.data.tableData;// 图表数据console.log(this.tableData);this.orderData = res.data.data.orderData;// orderData折线图数据this.userData=res.data.data.userData// 基于准备好的dom,初始化echarts实例// 获取dom元素,然后初始化echartsconst echartszx = echarts.init(this.$refs.echartszx);// 指定图表的配置和数据let zxOption = {};// 处理xAxis  x轴数据const xAxis = Object.keys(this.orderData.data[0]);const xAxisData = {data: xAxis};// x轴zxOption.xAxis = xAxisData;// y轴zxOption.yAxis = {};// 图例zxOption.legend = xAxisData;// y轴对应数据zxOption.series = [];xAxis.forEach(key => {zxOption.series.push({name: key,data: this.orderData.data.map(item => item[key]),type: "line"});});console.log(zxOption);// 使用刚指定的配置项和数据显示图表。echartszx.setOption(zxOption);// 柱状图const echartszzt = echarts.init(this.$refs.echartszzt);// 柱状图配置let zztOption = {legend: {// 图例文字颜色textStyle: {color: "#333"}},grid: {left: "20%"},// 提示框tooltip: {trigger: "axis"},xAxis: {type: "category", // 类目轴data: this.userData.map(item=>item.date),axisLine: {lineStyle: {color: "#17b3a3"}},axisLabel: {interval: 0,color: "#333"}},yAxis: [{type: "value",axisLine: {lineStyle: {color: "#17b3a3"}}}],color: ["#2ec7c9", "#b6a2de"],series: [{name:'阅读新增用户',data:this.userData.map(item=>item.new),type:'bar'},{name:'阅读活跃用户',data:this.userData.map(item=>item.active),type:'bar'}]};echartszzt.setOption(zztOption)});}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
// 右侧学习数据
.num {display: flex;flex-wrap: wrap; // 强制换行justify-content: space-between;.icon {// 图表width: 80px;height: 80px;font-size: 30px;text-align: center;line-height: 80px;color: white;}.detail {display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.dataPrice {font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.dataDesc {color: #999;font-size: 14px;text-align: center;}}.el-card {// 右侧卡片width: 32%;margin-bottom: 20px;}
}
.tbStyle {// 底部图表容器样式margin-top: 20px;display: flex;justify-content: space-between;
}
</style>

效果

7.2.3、饼图部分完善

接下来完成图表的最后一部分饼状图,相对于折线图和柱状图来说简单一些。
Home.vue

<template><el-row><!-- span是所占的比例 --><el-col :span="8" style="padding-right:10px"><el-card class="box-card"><div class="user"><!-- 用户信息 --><img src="../assets/tx.png" alt /><div class="userDetail"><p class="name">前端初见</p><p class="access">管理员</p></div></div><div class="user-info"><p>上次登录时间:<span>2023-3-29</span></p><p>上次登录地点:<span>中山</span></p></div></el-card><el-card style="margin-top: 20px; height:380px" class="box-card"><!-- 静态 --><!-- <el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="技术栈"></el-table-column><el-table-column prop="todayBuy" label="今日学习人数" ></el-table-column><el-table-column prop="monthBuy" label="本月学习人数"></el-table-column><el-table-column prop="totalBuy" label="学习总数据"></el-table-column></el-table>--><!-- 遍历生成表格 --><el-table :data="tableData" style="width: 100%"><el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val" /></el-table></el-card></el-col><el-col :span="16" style="padding-left:10px"><!-- 右侧区域 --><div class="num"><el-card:body-style="{display:'flex',padding:0}"v-for="item in countData":key="item.name"><i :style="{background:item.color}" class="icon" :class="`el-icon-${item.icon}`"></i><div class="detail"><p class="dataPrice">{{ item.value }}</p><p class="dataDesc">{{ item.name }}</p></div></el-card></div><el-card style="height:280px"><!-- 折线图 --><div ref="echartszx" style="height:280px"></div></el-card><div class="tbStyle"><el-card style="height:260px;width:48%;"><!-- 柱状图 --><div ref="echartszzt" style="height:260px;"></div></el-card><el-card style="height:260px;width:48%;"><!-- 饼图 --><div ref="echartsbt" style="height:250px;"></div></el-card></div></el-col></el-row>
</template>
<script>
import { getData } from "../api";
// 引入封装好的请求方法
import * as echarts from "echarts";
// 引入echarts
export default {data() {return {// tableData表格数据tableData: [],// orderData折线图数据orderData: [],// userData用户数据userData: [],// videoData视频数据videoData: [],// tableLabel根据表格整理表格表格label数据tableLabel: {name: "技术栈",todayBuy: "今日学习人数",monthBuy: "本月学习人数",totalBuy: "学习总数据"},// countData 学习数据统计countData: [{name: "今日学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "今日收藏文章",value: 210,icon: "star-on",color: "red"},{name: "今日查看数据",value: 1234,icon: "thumb",color: "chartreuse"},{name: "本月学习数据",value: 1234,icon: "s-data",color: "deeppink"},{name: "本月收藏文章",value: 210,icon: "star-on",color: "red"},{name: "本月查看数据",value: 1234,icon: "thumb",color: "chartreuse"}]};},mounted() {// mounted挂载getData().then(res => {// 获取res.data.data.tableData是返回的table数据this.tableData = res.data.data.tableData;// 图表数据console.log(this.tableData);this.orderData = res.data.data.orderData;// orderData折线图数据this.userData = res.data.data.userData;// userData 柱状图数据this.videoData = res.data.data.videoData;// videoData 饼图数据// 基于准备好的dom,初始化echarts实例// 获取dom元素,然后初始化echartsconst echartszx = echarts.init(this.$refs.echartszx);// 指定图表的配置和数据let zxOption = {};// 处理xAxis  x轴数据const xAxis = Object.keys(this.orderData.data[0]);const xAxisData = {data: xAxis};// x轴zxOption.xAxis = xAxisData;// y轴zxOption.yAxis = {};// 图例zxOption.legend = xAxisData;// y轴对应数据zxOption.series = [];xAxis.forEach(key => {zxOption.series.push({name: key,data: this.orderData.data.map(item => item[key]),type: "line"});});console.log(zxOption);// 使用刚指定的配置项和数据显示图表。echartszx.setOption(zxOption);// 柱状图const echartszzt = echarts.init(this.$refs.echartszzt);// 柱状图配置let zztOption = {legend: {// 图例文字颜色textStyle: {color: "#333"}},grid: {left: "20%"},// 提示框tooltip: {trigger: "axis"},xAxis: {type: "category", // 类目轴data: this.userData.map(item => item.date),axisLine: {lineStyle: {color: "#17b3a3"}},axisLabel: {interval: 0,color: "#333"}},yAxis: [{type: "value",axisLine: {lineStyle: {color: "#17b3a3"}}}],color: ["#2ec7c9", "#b6a2de"],series: [{name: "阅读新增用户",data: this.userData.map(item => item.new),type: "bar"},{name: "阅读活跃用户",data: this.userData.map(item => item.active),type: "bar"}]};echartszzt.setOption(zztOption);// 饼图const echartsbt = echarts.init(this.$refs.echartsbt);const btOption = {tooltip: {trigger: "item"},color: ["#0f78f4","#dd536b","#9462e5","#a6a6a6","#e1bb22","#39c362","#3ed1cf"],series: [{ data: this.videoData, type: "pie" }]};echartsbt.setOption(btOption)});}
};
</script>
<style lang="less" scoped>
.user {display: flex;align-items: center;border-bottom: 1px solid #ccc;padding-bottom: 20px;margin-bottom: 20px;img {margin-right: 40px;width: 150px;height: 150px;border-radius: 50%;}.userDetail {// 用户详情信息.name {// 用户名font-size: 32px;margin-bottom: 10px;}.access {// 角色color: #999;}}
}.user-info {// 登录时间和地点p {line-height: 28px;font-size: 14px;color: #999;span {color: #666;margin-left: 60px;}}
}
// 右侧学习数据
.num {display: flex;flex-wrap: wrap; // 强制换行justify-content: space-between;.icon {// 图表width: 80px;height: 80px;font-size: 30px;text-align: center;line-height: 80px;color: white;}.detail {display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.dataPrice {font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.dataDesc {color: #999;font-size: 14px;text-align: center;}}.el-card {// 右侧卡片width: 32%;margin-bottom: 20px;}
}
.tbStyle {// 底部图表容器样式margin-top: 20px;display: flex;justify-content: space-between;
}
</style>

效果

8、通用管理后台(面包屑和tag部分)

经过我们的完善,首页还剩顶部的一个面包屑功能待完善,接下来就完善这部分。之前我们在CommonHeader.vue写了一个静态的首页文字,接下来进行修改成我们真正的面包屑

总结

持续更新中

如果这篇【文章】有帮助到你

vue通用后台管理系统(保姆级)--持续更新中相关推荐

  1. VUE通用后台管理系统(一)登录

    先看效果 1)安装vue脚手架,项目配置,项目启动,这一步不做过多赘述,假设你已经成功启动了项目 2)配置路由 1.src/router/index.js中进行路由设置,代码如下 import Vue ...

  2. Shiro+Vue通用后台管理系统(附源码)

    目录 前言 一.项目介绍 1.运行 项目下载 2.技术栈 3.功能 4.角色权限介绍 二.流程讲解 三.数据库 E-R图设计 数据库脚本 四.系统搭建 项目结构 项目依赖 核心代码 1.JWT 工具类 ...

  3. 前端自学Vue笔记干货(第一版,持续更新中~~~)

    学习笔记 Vue笔记 nprogress使用 npm i nprogress -S 基本上都是在对axios进行二次封装.前置守卫路由或者封装成工具函数的.js文件中用到 import nprogre ...

  4. Java后台相关知识盘点(持续更新中)

    前言 本篇博客 取自于博主工作以来 对一些Java后台开发相关的 基础知识盘点和回顾, 主要涉及到Spring SpringBoot 和MyBatis相关的知识, 未来会一直更新下去- Spring ...

  5. 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Table表格增删查改、Pagination分页、搜索框

    文章目录 目标 代码 0.结构 1.按钮-删除 2.按钮-编辑 3.debug 4.样式 5.分页Pagination:功能 6.分页Pagination:样式 7.搜索框:功能 8.搜索框:样式 总 ...

  6. 【前端】Vue+Element UI案例:通用后台管理系统-登陆不同用户显示不同菜单、动态添加路由

    文章目录 目标 代码 0.动态地显示菜单:store 1.动态注册路由 2.解决刷新后摆平问题 总代码 本篇修改的代码文件 tab.js 参考视频: VUE项目,VUE项目实战,vue后台管理系统,前 ...

  7. 【前端】Vue+Element UI案例:通用后台管理系统-代码总结(已开源)

    文章目录 前言 项目文件目录 api mockServe home.js permission.js index.js mock.js user.js assert components Common ...

  8. 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出

    文章目录 目标 代码 0.页面结构 1.新增按钮和弹出表单:结构 2.新增按钮和弹出表单:点击新增弹出表单 3.表单样式 4.表单验证 5.表单的提交和取消功能:接口.mock相关准备 6.表单的提交 ...

  9. Thinkphp 6 + Vue 2 + ElementUI + Vxe-table 前后端分离的,一键生成代码和API接口的,通用后台管理系统 快速开发框架,开发小程序和APP的推荐框架!

    Thinkphp 6 + Vue 2 + ElementUI + Vxe-table 前后端分离的,一键生成代码和API接口的,通用后台管理系统 快速开发框架,开发小程序和APP的推荐框架! 概述 R ...

最新文章

  1. LCA ---- E. Tree Queries[LCA或者dfs序的解法]
  2. mysql定时备份并上传ftp_Linux下定时任务实现mysql自动备份并上传远程ftp
  3. 听说你想去大厂看妹子,带你看看腾讯产品运营岗超详细面经
  4. python计算一元一次方程的根_5-2 一元二次方程
  5. 《About Multi-Touch(多点触摸是个什么东西?)》:基于光学原理的多点触摸技术全解析...
  6. 【HDU - 4784】Dinner Coming Soon(记忆化搜索bfs,dp)
  7. springboot 手动提交事务_分布式事务开局第一篇,从数据库事务隔离级别说起
  8. System.arraycopy详解
  9. 第三:GitHub的使用(超详细)
  10. OOP in Javascript
  11. go并发编程之美(二)、go内存模型
  12. ppt矩形里面的图片怎么放大缩小_PPT5题目要求-矩形放大缩小
  13. vue 生成 证书模板 并支持 图片下载和导出PDF的demo
  14. 64位系统下同时使用64位和32位的eclipse
  15. 湘潭市古城中学《西游记》手抄报活动作品展示
  16. C++ vector去重 交集 并集
  17. 《零基础学Python》Python数据结构【四】
  18. python安装第三方库错误No matching distribution found for cfg
  19. maven-jar-plugin 插件打包jar配置
  20. 马尔可夫模型(MC, HMM, POMDP, MOMDP)

热门文章

  1. 防止浏览器缓存的办法
  2. 「十二省联考 2019」希望
  3. 福禄克FLUKE DSX2-8000 CH、DSX2-5000 CH、DSX-602 CH简易使用手册
  4. oracle组合索引最左原则,索引的最左前缀匹配原则的误区与索引下推技术
  5. aescfb加密_AES加密的几种工作模式
  6. 暑期学习日记27:js实现验证码生成与检验
  7. linux中initrd的含义,什么是initrd
  8. electron解压缩
  9. 【100DAYSCSS#90】追踪鼠标
  10. setscale方法的用法_【java】BigDecimal.setScale用法总结