likes
comments
collection
share

Vue2 + ElementUI = 后台管理系统

作者站长头像
站长
· 阅读数 17

1. vue脚手架

用来创建vue项目的工具包
创建项目:
    npm install -g vue-cli
    vue init webpack VueDemo
开发环境运行:
    cd VueDemo
    npm install
    npm run dev
生产环境打包发布(webpack打包)
    npm run build
    使用静态服务器工具包: 
    npm install -g serve
    serve dist
    http://localhost:5000

新建 vue.config.js

module.exports = {
  pages:{
    index:{
      entry:'src/main.js'
    }
  },
  lintOnSave:false//关闭语法检查
}

2 路由 vueRouter

  1. 在 src/router/index.js中写入路由信息,
  2. 在 src/App.vue 中使用路由组件, <router-view /> ,查看路由视图

· src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Main from '../views/Main.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Main',
    component: Main
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

· src/App.vue

<template>
  <div id="app">

     <router-link to="/">Main</router-link> |
     <router-link to="/about">about</router-link>

      <router-view />
  </div>
</template>

3 element-ui

引入element-ui

  • 全局引入
  • 按需引入(一般项目按需引入)
  1. 更改配置 babel.config.js @Babel/preset-env

· babel.config.js

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}
  1. 在Main.vue 中 引入 element-ui 的部分组件,需要在main.js 中注册 · main.js
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
// import ElementUI from 'element-ui';
// import 'element-ui/lib/theme-chalk/index.css';

// Vue.use(ElementUI);
import { Menu, MenuItem, MenuItemGroup,Submenu,Button, Select,Radio,Container,Aside,Header,Main } from 'element-ui';

Vue.use(Button);
Vue.use(Select);
Vue.use(Radio);
Vue.use(Container);
Vue.use(Aside);
Vue.use(Header);
Vue.use(Menu);
Vue.use(Main);
Vue.use(MenuItem);
Vue.use(MenuItemGroup);
Vue.use(Submenu);
// Vue.component(Button.name, Button);
// Vue.component(Select.name, Select);
// Vue.component(Radio.name, Radio);


Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

4 Container 布局

Vue2 + ElementUI = 后台管理系统 src/views/Main.vue

<template>
  <el-container style="height:100%">
  <el-aside width="auto">aside</el-aside>
  <el-container>
    <el-header>Header</el-header>
    <el-main>Main</el-main>
  </el-container>
</el-container>
</template>

5 组件

侧边栏组件UI-CommonAside.vue

Vue2 + ElementUI = 后台管理系统

  1. 创建侧边栏组件src/components/CommonAside.vue, 复制粘贴侧边栏组件UI。
  2. 在src/views/Main.vue中引入组件import CommonAside from "../components/CommonAside.vue",注册组件components: {CommonAside} 并使用组件标签<common-aside></common-aside>

· src/components/CommonAside.vue

<template> 
    <el-menu  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>

一级菜单和二级菜单

Vue2 + ElementUI = 后台管理系统 src/components/CommonAside.vue

  1. 写入菜单数据
  2. 计算属性,判断 有无子菜单
  3. 通过:band 和 v-for 去绑定和循环,实现菜单列表
<template> 
    <el-menu class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse">
        <h3>通用后台管理系统</h3>
        <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.label" v-for="item in hasChildren" :key="item.path">
            <template slot="title">
            <i :class="'el-icon-'+ item.icon"></i>
            <span slot="title">{{item.label}}</span>
            </template>
            <el-menu-item-group>
            <el-menu-item :index="subItem.path" v-for="(subItem, subIndex) in item.children" :key="subIndex">
            <i :class="'el-icon-'+ subItem.icon"></i>
            <span slot="title">{{subItem.label}}</span> 
            </el-menu-item>
            </el-menu-item-group>
        </el-submenu>
    </el-menu>
</template> 

<style scoped>
  .el-menu-vertical-demo:not(.el-menu--collapse) {
    width: 200px;
    min-height: 400px;
    height: 100vh;
    border-right: 0;
  }
  .el-menu-item, .el-submenu{
    text-align: left;
  }
</style>

<script>
  export default {
    data() {
      return {
        isCollapse: true,
        //菜单   1 写入菜单数据
        menu:[
            {
                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);
      }
    },
            //   2 计算属性,判断 有无子菜单
    computed: {
        noChildren() {
            return this.menu.filter((item) => !item.children)
        },
        hasChildren(){
            return this.menu.filter((item) => item.children)
        }
    }
  }
</script>

Menu 样式

src/components/CommonAside.vue

    <el-menu 
    class="el-menu-vertical-demo"  
    :collapse="isCollapse" 
    background-color="#545c64" 
    text-color="fff" 
    active-text-color="#ffd04b">

CommonHeader 组件(下拉列表)

  1. 创建Header组件src/components/CommonHeader.vue , 复制粘贴el-header组件UI。

  2. 在src/views/Main.vue中引入组件import CommonHeader from "../components/CommonHeader.vue",注册组件components: {CommonHeader}

并使用组件标签<common-header>首页</common-header>

· src/components/CommonHeader.vue

<template>
    <header>
    <div class="l-content">
        <el-button plain icon="el-icon-menu" size="mini"></el-button>   
        <h3 style="color:#fff"></h3>
    </div>
    <div class="r-content">
        <el-dropdown trigger="click" size="mini">
            <span class="el-dropdown-link">
                <img src="userImg" class="user" />
            </span>
             <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>个人中心</el-dropdown-item>
            <el-dropdown-item>退出</el-dropdown-item>
        </el-dropdown-menu>
        </el-dropdown>
    </div>
    </header>
</template>

<script>

export default {
  components: {
  },
  // 定义属性
  data() {
    return {
      userImg: require('../assets/user.png'),
    }
  },
  // 计算属性,会监听依赖属性值随之变化
  computed: {
  },
  // 监控data中的数据变化
  watch: {},
  // 方法集合
  methods: {
    
  },
}
</script>

<style lang='scss' scoped>
  header {
      display: flex;
      height:100%;
      align-items: center;
      justify-content:space-between;

  }
  .l-content{
      display:flex;
      align-item:center;
      .el-button{
          margin-right:20px
      }
  }
  .r-content{
      .user{
          width:40px;
          height:40px;
          border-radio: 50%;
      }
  }
</style>

侧边栏的折叠收缩(Vuex)

说明:点击 header ,实现侧边栏的折叠收缩

注意:这里使用 Vuex 状态管理,实现了跨组件传值 ,

· 1 store/tab.js

export default{
    state:{
        isCollapse: false,
    },
    mutations:{
        collapseMenu(state){
            state.isCollapse = !state.isCollapse;
        }
    }
    
}

·2 store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'//当前目录下的tab

Vue.use(Vuex)

export default new Vuex.Store({
 
  modules: {
    tab
  }
})

3.将isCollapse改为方法,去判断是否折叠 // isCollapse: false,//这里写在computed中

· src/components/CommonAside.vue

 <el-menu class="el-menu-vertical-demo"  :collapse="isCollapse" background-color="#545c64" text-color="fff" active-text-color="#ffd04b">
        <h3 v-show="!isCollapse">通用后台管理系统</h3>
        <h3 v-show="isCollapse">后台</h3>
computed: {
        noChildren() {
            return this.menu.filter((item) => !item.children)
        },
        hasChildren(){
            return this.menu.filter((item) => item.children)
        },
        isCollapse(){
          return this.$store.state.tab.isCollapse
        }
    }

· src/components/CommonHeader.vue

        <el-button plain icon="el-icon-menu" size="mini" @click="clickHandler(item)"></el-button>   


 methods: {
    clickHandler(){
        this.$store.commit('collapseMenu');
    }
  },

home 组件布局 (router)

  1. router/index.js 加入子路由
const routes = [
  {
    path: '/',
    name: 'Main',
    component: Main,
    children: [
      {
        path: '/',
        name:'home',
        component: ()=>import('@/views/Home/Home')//路由懒加载
      }
    ]
  },
  1. 新建Home组件 views/Home/Home.vue
<template>
  <div>
      这是Home的组件
  </div>
</template>

home组件用户卡片部分

Vue2 + ElementUI = 后台管理系统

<template>
  <el-row class="home" :gutter="20">
    <el-col :span="8" style="margin-top:20px">
        <el-card shadow="hover">
            <div class="user">
                <img :src="userImg"/>
                <div class="userinfo">
                    <p class="name">Admin</p>
                    <p class="access">超级管理员</p>
                </div>
            </div>
            <div class="login-info">
            <p>上次登录时间: <span>2021-12-20</span></p>
            <p>上次登录地点: <span>苏州</span></p>

            </div>
        </el-card>
    </el-col>
    <el-col :span="16"></el-col>
  </el-row>
</template>

<script>

export default {
  components: {
  },
  // 定义属性
  data() {
    return {
          userImg:require('../../assets/user.png')
        }
    },
  // 计算属性,会监听依赖属性值随之变化
  computed: {
  },
  // 监控data中的数据变化
  watch: {},
  // 方法集合
  methods: {
    
  },
}

</script>

<style lang='sass' scoped>
  @import "~@/assets/scss/home"
</style>

home 组件购买统计

Vue2 + ElementUI = 后台管理系统

 <el-card style="margin-top:20px">
            <el-table :data="tableData">
            <el-table-column 
            show-overflow-tooltip 
            v-for="(val,key) in tableLabel"
            :key="key"
            :prop="key"
            :label="val"
            >
             </el-table-column>
            </el-table>
        </el-card>
data() {
    return {
          userImg:require('../../assets/user.png'),
          tableData:[],//这里需要放入数据,或者mock虚拟数据
          tableLabel:{
              name: '课程',
              todayBuy:'今天购买',
              mouthBuy:'本月购买',
              totalBuy:'总购买',
          }
        }
    },

home 组件订单展示

Vue2 + ElementUI = 后台管理系统

 <el-col :span="16" style="margin-top:20px">
    <div class="num">
        <el-card 
            shadow="hover" 
            v-for="item in countData" 
            :key="item.name" 
            :body-style="{display:'flex',padding:0}"
            >
        <i 
            class="icon" 
            :class="`el-icon-${item.icon}` " 
            :style="{background: item.color}"
            ></i>
        <div class="detail">
            <p class="num">¥{{item.value}}</p>
            <p class="txt">{{item.name}}</p>
        </div>
        </el-card>
        </div>
        <el-card shadow="hover" style="height:280px"></el-card>
        <div class="graph" >
            <el-card shadow="hover" style="height:260px"></el-card>
            <el-card shadow="hover" style="height:260px"></el-card>
        </div>
    </el-col>

axios 基本使用

  1. 下载安装axios cnpm install axios

main.js

import https from 'axios'

Vue.prototype.$http = http

2 Home.vue

mounted() {
        this.$http.get('/user?ID=12345')
            .then(function (response) {
                console.log(response);
            })
            .catch(function (error) {
                console.log(error);
            });
    }

二次封装的axios的实现(拦截器)

在请求之前(添加一些参数)、请求之后,加入拦截器,可以对公共错误进行集中处理。

  1. 新建 src/api/axios.js
// 拦截器
import axios from 'axios';
//引入config/index.js
import config from '../config/index'
//设置配置 开发环境和生产环境不一样
const baseUrl  = process.env.NODE_ENV ===  'development' ? config.baseUrl.dev :config.baseUrl.pro

class HttpRequest {
    constructor(baseUrl){
        this.baseUrl = baseUrl
    }
    getInsideConfig() { 
        const config = {
            baseUrl:this.baseUrl,
            header:{

            }
        }
        return config
    }
    interceptors(instance) {
        // 添加请求拦截器
        instance.interceptors.request.use(function (config) {
            // 在发送请求之前做些什么
            console.log('拦截处理请求')
            return config;
        }, function (error) {
            // 对请求错误做些什么
            return Promise.reject(error);
        });

        // 添加响应拦截器
        instance.interceptors.response.use(function (response) {
            // 对响应数据做点什么
            console.log('处理响应')
            return response.data;
        }, function (error) {
            // 对响应错误做点什么
            console.log(error)
            return Promise.reject(error);
        });
    }
    // {
    //     baseUrl:'./api-'
    // }
    request(options){//request(option)是调用上面HttpRequest的方法,options是传入一些参数对象
        //请求
        //  /api/getList  /api/getHome
        const instance = axios.create()
        options = {...(this.getInsideConfig()),...options}//技巧
        this.interceptors(instance)
        return instance(options)
    }
}

export default new HttpRequest(baseUrl)

options = {...(this.getInsideConfig()),...options}

  1. 新建src/config/index.js config 这里的事api/axios里面的一些配置

export default{
    title:'admin',
    baseUrl:{
        //开发环境
        dev:'./api/',
        // 生产环境
        pro:''
    }
}

二次封装的axios的用法

  1. 新建 scr/api/data.js
import axios from './axios'

export const getMenu = () => {
    return axios.request({
        url:'menu',
        method:'GET',
    })
}

2 Home.vue 引入并使用

<script>
import {} from "../../api/data.js"


    mounted() {
        getMenu().then((res)=>{
            console.log(res)
        })
    }
}
</script>

mock 数据模拟的实现

mock.js 生成随机数据,拦截ajax请求

1.安装 cnpm i mockjs -S

  1. 新建 api/mock.js
import Mock from "mockjs"
import homeApi from "./mockServerData/home"
Mock.mock('api/home/getData',homeApi.getStatisticalData)
  1. 新建 api/mockServerData/home.js 导入数据

  2. api/data.js 添加getHome方法

import axios from './axios'

export const getMenu = () => {
    return axios.request({
        url:'menu',
        method:'GET',
    })
}

export const getHome = () => {
    return axios.request({
        url:'/home/getData',
        method:'GET',
    })
}
  1. main.js 中引入mock.js
if(process.env.NODE_EN === 'development') require('@/api/mock')

  1. 使用mock

Echarts 应用

Vue2 + ElementUI = 后台管理系统

  1. 安装echarts
  2. 加入ref="userEchart" Home.vue
<el-card shadow="hover" style="height:280px" ></el-card>
        <div style="height:280px" ref="echart"></div>
        <div class="graph" >
            <el-card shadow="hover" style="height:260px"></el-card>
                <div style="height:280px" ref="userEchart"> </div>

            <el-card shadow="hover" style="height:260px"></el-card>
              <div style="height:280px" ref="videoEchart"> </div>
  1. 展示

    methods:{
        getTableData(){
            getHome().then((res)=>{
                this.tableData = res.data.tableData

                // 折线图的展示
                const order = res.data.orderData
                // console.log(order)
                this.echartsData.order.xAxis.data = order.data

                let keyArray = Object.keys(order.data[0])
                keyArray.forEach((key)=>{
                    this.echartsData.order.series.push({
                        name: key,
                        data: order.data.map((item) => item[key]),
                        type: "line",
                        })
                })
                const myEchartsOrder = echarts.init(this.$refs.echart)//refs不要直接写在el-组件上,用div
                myEchartsOrder.setOptions(this.echartsOrder.order)

                // 用户图
                this.echartsData.user.xAxis.data = red.data.userData.map((item)=> item.data)
                this.echartsData.user.series.push({
                    name:'新增用户',
                    data: res.data.userData.map((item) => item.new),
                    type:'bar'
                });
                this.echartsData.user.series.push({
                    name:'活跃用户',
                    data: res.data.userData.map((item) => item.active),
                    type:'bar'
                });

                const myEchartsUser = echarts.init(this.$refs.userEchart)//refs不要直接写在el-组件上,用div
                myEchartsUser.setOptions(this.myEchartsUser.user)

                // 饼状图
                this.echartsData.video.series.push({
                    data: res.data.videoData,
                    type:'pie'
                });
                const myEchartsVideo = echarts.init(this.$refs.videoEchart)//refs不要直接写在el-组件上,用div
                myEchartsVideo.setOptions(this.myEchartsVideo.user)

            })
        }
    },

Echarts 封装

Vue2 + ElementUI = 后台管理系统 1.新建components/ECharts.vue组件 根据是否有x轴,来区分

<template>
  <div ref="echart">
      
  </div>
</template>

<script>

export default {
  components: {
  },
  props:{
      charData: {
          type: Object,
          default(){
              return{
                  xData:[],
                  series,
              }
          }
      },
      isAxisChart: {
          type: Boolean,
          default: true,
      }
  }
  // 定义属性
  data() {
    return {
      axisOption:{ 
          legend: {
          // 图例文字颜色
          textStyle: {
            color: "#333",
          },
        },
        grid: {
          left: "20%",
        },
        // 提示框
        tooltip: {
          trigger: "axis",
        },
        xAxis: {
          type: "category", // 类目轴
          data: [],
          axisLine: {
            lineStyle: {
              color: "#17b3a3",
            },
          },
          axisLabel: {
            interval: 0,
            color: "#333",
          },
        },
        yAxis: [
          {
            type: "value",
            axisLine: {
              lineStyle: {
                color: "#17b3a3",
              },
            },
          },
        ],
        color: ["#2ec7c9", "#b6a2de", "#5ab1ef", "#ffb980", "#d87a80", "#8d98b3"],
        series: [],
      },
      normalOption: {
        tooltip: {
          trigger: "item",
        },
        color: [
          "#0f78f4",
          "#dd536b",
          "#9462e5",
          "#a6a6a6",
          "#e1bb22",
          "#39c362",
          "#3ed1cf",
        ],
        series: [],
      },
      echart: null,
    };
  },

  // 计算属性,会监听依赖属性值随之变化
  computed: {
  },
  // 监控data中的数据变化
  watch: {},
  // 方法集合
  methods: {
    
  },
}
</script>

<style lang='sass' scoped>
  
</style>
  1. 判断不一样的data,通过watch监听
  // 2 监控data中的数据变化
  watch: {
      charData{
          handler:function(){
              this.initChart();
          },
          deep:true,
      }
  },

3、4.init初始化chart

// 方法集合
  methods: {
    initChart() {
      this.initChartData();
      // 设置echarts表格了
      //
      if (this.echart) {
        this.echart.setOption(this.options);
      } else {
        this.echart = echarts.init(this.$refs.echart);
        this.echart.setOption(this.options);
      }
    },
    initChartData() {
      if (this.isAxisChart) {
        this.axisOption.xAxis.data = this.chartData.xData;
        this.axisOption.series = this.chartData.series;
      } else {
        this.normalOption.series = this.chartData.series;
      }
    },
  },

5 计算属性,会监听依赖属性值随之变化

   // 5 计算属性,会监听依赖属性值随之变化
  computed: {
      options(){
          return this.isAxisChart ? this.axisOption :this.normalOption
      }
  },

封装折线图、饼状图和条形图

home.vue

 <el-card shadow="hover" style="height: 280px">
        <echart :chartData="echartData.order" style="height: 280px"></echart>
      </el-card>
      <div class="graph">
        <el-card shadow="hover" style="height: 260px">
          <echart :chartData="echartData.user" style="height: 240px"></echart>
        </el-card>
        <el-card shadow="hover" style="height: 260px">
          <echart
            :chartData="echartData.video"
            style="height: 240px"
            :isAxisChart="false"
          ></echart>
        </el-card>
 methods: {
    getTableData() {
      getHome().then((res) => {
        // console.log(res);
        this.tableData = res.data.tableData;

        // 折线图的展示
        const order = res.data.orderData;
        let keyArray = Object.keys(order.data[0]);

        // const myEchartsOrder = echarts.init(this.$refs.echart);
        // myEchartsOrder.setOption(this.echartsData.order);

        // 传给组件的值
        this.echartData.order.xData = order.date;
        keyArray.forEach((key) => {
          this.echartData.order.series.push({
            name: key,
            data: order.data.map((item) => item[key]),
            type: "line",
          });
        });

        // 用户图

        this.echartData.user.xData = res.data.userData.map((item) => item.date);
        this.echartData.user.series.push({
          name: "新增用户",
          data: res.data.userData.map((item) => item.new),
          type: "bar",
        });
        this.echartData.user.series.push({
          name: "活跃用户",
          data: res.data.userData.map((item) => item.active),
          type: "bar",
        });

        this.echartData.video.series.push({
          data: res.data.videoData,
          type: "pie",
        });
      });
    },
  },
  mounted() {
    this.getTableData();
  },
};