VUE 通用后台管理系统

项目简介

实现功能

用VUE框架搭建一个通用后台管理系统,组件化、模块化实现用户登陆、后台管理、页面显示和跳转等功能。
在这里插入图片描述

技术栈

在这里插入图片描述

参考文档

vue通用后台管理系统(一)
vue通用后台管理系统(二)
vue通用后台管理系统(三)
vue通用后台管理系统(四)

实现流程

环境配置

Vue安装及环境配置、开发工具_vue开发工具

创建项目
查看环境配置

在这里插入图片描述

创建项目

创建:vue create my-app
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

更改命名规范设置

![[屏幕截图 2023-12-04 123016.png]]

导入Element-UI
官网网址

Element - 网站快速成型工具

安装

注意版本的兼容性,这里选择安装2.15.13版本
![[Pasted image 20231204112854.png]]

导入
//导入Element-UI
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI)

![[Pasted image 20231204113857.png]]

测试

![[Pasted image 20231204114432.png]]

![[Pasted image 20231204114532.png]]

less

cnpm i less@4.1.2 npm i less-loader@6.0.0
![[Pasted image 20231204173305.png]]

Vuex

安装:cnpm i vuex@3.6.2

Anxios

安装:cnpm install axios@0.27.2

Echarts

安装:cnpm i echarts@5.1.2

Vue-Router
官网

Vue Router

安装

cnpm i vue-router@3.6.5
![[Pasted image 20231204115258.png]]

测试
创建相关文件目录文件和组件

![[屏幕截图 2023-12-04 145310 1.png]]

router文件下的index.js代码
import Vue from 'vue'
import VueRouter from 'vue-router'
//导入组件
import Home from '../views/Home.vue'
import User from '../views/User.vue' 
//使用vuerouter
Vue.use(VueRouter)
//路由与组件映射
const routes = [
    { path: '/home', component: Home },
    { path: '/user', component: User }
]
//创建router实例
const router = new VueRouter({
    routes,
})
//对外暴露路由
export default router
在main.js中进行导入和挂载

![[屏幕截图 2023-12-04 145945.png]]

//导入router
import router from './router'

new Vue({
  //挂载router
  router,
  render: h => h(App),
}).$mount('#app')
在需要渲染的地方(App.vue)添加代码

<router-view></router-view>

嵌套路由
创建Main组件并在router的index.js文件中修改路由

![[屏幕截图 2023-12-04 151351.png]]

import Main from '../views/Main.vue'

const routes = [
    {
        path: '/',
        component: Main,
        children: [
            { path: 'home', component: Home },
            { path: 'user', component: User }
        ]
    }
]
在Main组件中设置路由出口

![[Pasted image 20231204152333.png]]

Container布局容器
结构显示框图

![[Pasted image 20231204153631.png]]

Main.vue代码

![[屏幕截图 2023-12-04 153743 1.png]]

    <el-container>
      <el-aside width="200px">Aside</el-aside>
      <el-container>
        <el-header>Header</el-header>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
左侧菜单栏的引入
创建CommonAside组件

将ElementUI中的导航菜单导入进来,注意一个容器内只允许容纳一个ElementUI标签。

在Main组件中导入并渲染

![[屏幕截图 2023-12-04 162002.png]]

根据menuData对菜单栏进行调整
在CommonAside组件中导入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'
                            }
                        ]
                    }
                ]
计算属性分类menuData
computed: {
        //没有子菜单
        noChildren() {
            return this.menuData.filter(item => !item.children)
        },
        //有子菜单
        hasChildren() {
            return this.menuData.filter(item => item.children)
        },
    }
对menuData中的对象进行遍历的代码
<template>
    <el-menu 
    default-active="1-4-1" 
    class="el-menu-vertical-demo" 
    @open="handleOpen" 
    @close="handleClose"
    :collapse="isCollapse" 
    background-color="#454545" 
    text-color="#ffffff" 
    active-text-color="#00ffff">
        <!-- 对menuData中无子节点的对象进行遍历 -->
        <el-menu-item v-for="item in noChildren" :key="item.name" :index="item.name">
            <i :class="`el-icon-${item.icon}`"></i>
            <span slot="title">{{ item.label }}</span>
        </el-menu-item>
        <!-- 对menuData中有子节点的对象进行遍历 -->
        <el-submenu v-for="item in hasChildren" :key="item.name" :index="item.name">
            <template slot="title">
                <i :class="`el-icon-${item.icon}`"></i>
                <span slot="title">{{ item.label }}</span>
            </template>
            <!-- <el-menu-item v-for="subItem in item.children" :key="subItem.path" :index="subItem.path">{{ subItem.label }}</el-menu-item> -->
            <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>
样式调整
CommonAside.vue
<style lang="less" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
    width: 200px;
    min-height: 400px;
}
.el-menu{
    border-right: none;
    height: 100vh;
        //less样式
        h3 {
        color: #fff;
        text-align: center;
        line-height: 48px;
        font-size: 16px;
        font-weight: 400;
    }
}
</style>
Main.vue
<style lang="less" scoped>
el-header{
  padding: 0;
}
</style>
App.vue
<!-- 清除边框 -->
<style lang="less">
  html, body, h3{
    margin:0px;
    padding: 0px;
  }
</style>
效果

![[Pasted image 20231204175502.png]]

实现点击菜单栏进行路由跳转

![[屏幕截图 2023-12-04 203236.png]]
在mothods中添加方法

clickMenu(item){
//跳转前判断路由是否合法
if(this.$route.path !== item.path && !(this.$route.path === '/home' && item.path == '/')){
            this.$router.push(item.path);
            }
        }
创建CommonHeader组件并导入

![[屏幕截图 2023-12-04 204327.png]]

CommonHeader模板代码及其基本样式
<template>
    <div class="headerContainer">
         <div class="l-content">
            <el-button style="margin-right:20px" icon="el-icon-menu" size="mini"></el-button>
            <el-breadcrumb separator="/">
                <el-breadcrumb-item>首页</el-breadcrumb-item>
            </el-breadcrumb>
        </div>
        <div class="r-content">
            <el-dropdown>
                <span class="el-dropdown-link">
                    <img class="user" src="../assets/images/user.png" alt="">
                </span>
                <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>
</script>

<style lang="less" scoped>
.headerContainer{
    padding: 0 20px;
    background-color: #333;
    height: 60px;
    display: flex;
    justify-content: space-between;//左右分离
    align-items: center;//垂直居中
    .l-content{
        display: flex;
        align-items: center;
        /deep/.el-breadcrumb__item {
            .el-breadcrumb__inner {
                font-weight: normal;
                &.is-link{
                   color: #999
               }
            }
            &:last-child {
                .el-breadcrumb__inner {
                    color: #FFF
                }
            }
        }
    }
    .r-content{
        .user {
            width: 40px;
            height: 40px;
            border-radius: 50%;
        }
    }
    .text {
        color: #fff;
        font-size: 14px;
        margin-left: 10px;
    }
}
</style>
组件通信-vuex
创建store目录及其index.js和tab.js文件

![[Pasted image 20231204222333.png]]

index.js代码
import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'
Vue.use(Vuex)
export default new Vuex.Store({
    modules:{
        tab,
    }
})
tab.js代码
export default {
    state:{
    },
    mutations: {
    }
}
导入并挂载store

![[屏幕截图 2023-12-04 222458.png]]

组件通信方式实现菜单栏的展开折叠
vuex部分

![[屏幕截图 2023-12-04 225146.png]]

CommonHeader组件部分

![[屏幕截图 2023-12-04 225424.png]]

![[屏幕截图 2023-12-04 225542.png]]

CommonAside部分

![[Pasted image 20231204225755.png]]

![[屏幕截图 2023-12-04 225824.png]]

系统名在折叠时的显示

<h3>{{ isCollapse ? '后台':'通用后台管理系统' }}</h3>

首页
Layout布局
<template>
  <el-row>
    <el-col :span="8">
      <div ></div>
    </el-col>
    <el-col :span="16">
      <div ></div>
    </el-col>
  </el-row>
</template>
Home组件基本模板及左上角名片代码
<template>
  <el-row>
    <el-col :span="8">
      <el-card class="box-card">
        <div class="user">
          <img src="../assets/images/user.png" alt="">
          <div class="userinfo">
            <p class="name">Admin</p>
            <p class="access">管理员</p>
            <p></p>
          </div>
        </div>
        <div class="login-info">
          <p>上次登陆地点: <span>广东 珠海</span></p>
          <p>上次登陆时间: <span>20231224</span></p>
        </div>
      </el-card>
      <el-zard style="margin-top: 20px; height: 460px"></el-card
    </el-col>
    <el-col :span="16">
      <div ></div>
    </el-col>
  </el-row>
</template>
<script>
export default {
  name: 'Home',
  data() {
    return {
    }
  }
}
</script>
<style lang="less" scoped>
.user{
  display: flex;
  align-items: center;
  padding-bottom: 20px;
  margin-bottom: 20px;
  img{
    margin-right: 40px;
    width: 150px;
    height: 150px;
    border-radius: 50%;
  }
  .userinfo {
    .name {
      font-size: 32px;
      margin-bottom: 10px;
    }
    .access{
      color: #999999;
    }
  }
}
.login-info {
  p {
    line-height: 28px;
    font-size: 14px;
    color:#999999;
    span {
      color: #666;
      margin-left: 60px;
    }
  }
}
</style>
右侧订单的实现
countData数据
countData: [
        {
          name: "今日支付订单",
          value: 1234,
          icon: "success",
          color: "#2ec7c9",
        },
        {
          name: "今日收藏订单",
          value: 210,
          icon: "star-on",
          color: "#ffb980",
        },
        {
          name: "今日未支付订单",
          value: 1234,
          icon: "s-goods",
          color: "#5ab1ef",
        },
        {
          name: "本月支付订单",
          value: 1234,
          icon: "success",
          color: "#2ec7c9",
        },
        {
          name: "本月收藏订单",
          value: 210,
          icon: "star-on",
          color: "#ffb980",
        },
        {
          name: "本月未支付订单",
          value: 1234,
          icon: "s-goods",
          color: "#5ab1ef",
        },
      ],
代码实现
<div class="num">
        <el-card 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="price">{{ item.value }}</p>
            <p class="desc">{{ item.name }}</p>
          </div>
        </el-card>
      </div>
Axios
创建utils目录及其request.js文件
import axios from 'axios'
const http = axios.create({
    baseURL: '/api',
    timeout: 10000,
})
// 添加请求拦截器
http.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });
// 添加响应拦截器
http.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });
export default http
创建api目录及其index.js文件
//api文件夹中放置关于接口数据的信息
import http from '../utils/request'
//请求首页数据
export const getData = () => {
    //返回一个Promise对象
    return http.get('/home/getData')
}
Mock数据模拟
安装

cnpm i mockjs@1.1.0

配置后端数据模拟

![[Pasted image 20231205092011.png]]

mock.js代码
import Mock from 'mockjs'
import homeApi from './mockServeData/home'
import user from './mockServeData/user'
import permission from './mockServeData/permission'

Mock.mock('/api/home/getData', homeApi.getStatisticalData);

//用户列表的数据
Mock.mock('/api/user/add', 'post', user.createUser);
Mock.mock('/api/user/edit', 'post', user.updateUser);
Mock.mock('/api/user/del', 'post', user.deleteUser);
Mock.mock(/api\/user\/get/, user.getUserList);
Mock.mock(/api\/permission\/getMenu/, 'post', permission.getMenu)
在main.js文件中引入

import './api/mock'

home.js代码
// 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
          }
        ]
      }
    }
  }
}
permission.js代码
import Mock from 'mockjs'
export default {
  getMenu: config => {
    const { username, password } = JSON.parse(config.body)
    // 先判断用户是否存在
    // 判断账号和密码是否对应
    if (username === 'admin' && password === 'admin') {
      return {
        code: 20000,
        data: {
          menu: [
            {
              path: '/home',
              name: 'home',
              label: '首页',
              icon: 's-home',
              url: 'Home.vue'
            },
            {
              path: '/mall',
              name: 'mall',
              label: '商品管理',
              icon: 'video-play',
              url: 'Mall.vue'
            },
            {
              path: '/user',
              name: 'user',
              label: '用户管理',
              icon: 'user',
              url: 'User.vue'
            },
            {
              label: '其他',
              icon: 'location',
              children: [
                {
                  path: '/page1',
                  name: 'page1',
                  label: '页面1',
                  icon: 'setting',
                  url: 'PageOne.vue'
                },
                {
                  path: '/page2',
                  name: 'page2',
                  label: '页面2',
                  icon: 'setting',
                  url: 'PageTwo.vue'
                }
              ]
            }
          ],
          token: Mock.Random.guid(),
          message: '获取成功'
        }
      }
    } else if (username === 'xiaoxiao' && password === 'xiaoxiao') {
      return {
        code: 20000,
        data: {
          menu: [
            {
              path: '/home',
              name: 'home',
              label: '首页',
              icon: 's-home',
              url: 'Home.vue'
            },
            {
              path: '/video',
              name: 'video',
              label: '商品管理',
              icon: 'video-play',
              url: 'Mall.vue'
            }
          ],
          token: Mock.Random.guid(),
          message: '获取成功'
        }
      }
    } else {
      return {
        code: -999,
        data: {
          message: '密码错误'
        }
      }
    }
  }
}
user.js代码
import Mock from 'mockjs'
// get请求从config.url获取参数,post从config.body中获取参数
function param2Obj (url) {
  const search = url.split('?')[1]
  if (!search) {
    return {}
  }
  return JSON.parse(
    '{"' +
    decodeURIComponent(search)
      .replace(/"/g, '\\"')
      .replace(/&/g, '","')
      .replace(/=/g, '":"') +
    '"}'
  )
}
let List = []
const count = 200
for (let i = 0; i < count; i++) {
  List.push(
    Mock.mock({
      id: Mock.Random.guid(),
      name: Mock.Random.cname(),
      addr: Mock.mock('@county(true)'),
      'age|18-60': 1,
      birth: Mock.Random.date(),
      sex: Mock.Random.integer(0, 1)
    })
  )
}
export default {
//   /**
//    * 获取列表
//    * 要带参数 name, page, limt; name可以不填, page,limit有默认值。
//    * @param name, page, limit
//    * @return {{code: number, count: number, data: *[]}}
//    */
  getUserList: config => {
    const { name, page = 1, limit = 20 } = param2Obj(config.url)
    console.log('name:' + name, 'page:' + page, '分页大小limit:' + limit)
    const mockList = List.filter(user => {
      if (name && user.name.indexOf(name) === -1 && user.addr.indexOf(name) === -1) return false
      return true
    })
    const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
    return {
      code: 20000,
      count: mockList.length,
      list: pageList
    }
  },
  /**
   * 增加用户
   * @param name, addr, age, birth, sex
   * @return {{code: number, data: {message: string}}}
   */
  createUser: config => {
    const { name, addr, age, birth, sex } = JSON.parse(config.body)
    console.log(JSON.parse(config.body))
    List.unshift({
      id: Mock.Random.guid(),
      name: name,
      addr: addr,
      age: age,
      birth: birth,
      sex: sex
    })
    return {
      code: 20000,
      data: {
        message: '添加成功'
      }
    }
  },
  /**
   * 删除用户
   * @param id
   * @return {*}
   */
  deleteUser: config => {
    const { id } = JSON.parse(config.body)
    if (!id) {
      return {
        code: -999,
        message: '参数不正确'
      }
    } else {
      List = List.filter(u => u.id !== id)
      return {
        code: 20000,
        message: '删除成功'
      }
    }
  },
  /**
   * 批量删除
   * @param config
   * @return {{code: number, data: {message: string}}}
   */
  batchremove: config => {
    let { ids } = param2Obj(config.url)
    ids = ids.split(',')
    List = List.filter(u => !ids.includes(u.id))
    return {
      code: 20000,
      data: {
        message: '批量删除成功'
      }
    }
  },
  /**
   * 修改用户
   * @param id, name, addr, age, birth, sex
   * @return {{code: number, data: {message: string}}}
   */
  updateUser: config => {
    const { id, name, addr, age, birth, sex } = JSON.parse(config.body)
    const sex_num = parseInt(sex)
    List.some(u => {
      if (u.id === id) {
        u.name = name
        u.addr = addr
        u.age = age
        u.birth = birth
        u.sex = sex_num
        return true
      }
    })
    return {
      code: 20000,
      data: {
        message: '编辑成功'
      }
    }
  }
}
Home.vue代码
<template>
    <el-row>
        <el-col :span="8" style="padding-right: 10px">
            <el-card class="box-card">
                <div class="user">
                    <img src="../assets/images/user.png" alt="">
                    <div class="userinfo">
                        <p class="name">Admin</p>
                       <p class="access">SuperAdmin</p>
                    </div>
                </div>
                <div class="login-info">
                    <p>上次登录时间:<span>20231123</span></p>
                    <p>上次登录地点:<span>珠海</span></p>
                </div>
            </el-card>
            <el-card style="margin-top:20px; height:460px">
                <el-table :data="tableData" style="width: 100%">
                    <!-- <el-table-column prop="name" label="课程" width="180">
                    </el-table-column>
                    <el-table-column prop="todayBuy" label="今日购买" width="180">
                    </el-table-column>
                    <el-table-column prop="monthBuy" label="本月购买" width="180">
                    </el-table-column>
                    <el-table-column prop="totalBuy" label="总购买" width="180">
                    </el-table-column> -->
                    <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 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="price">{{ item.value }}</p>
                        <p class="desc">{{ item.name }}</p>
                    </div>
                </el-card>
            </div>
            <el-card style="height: 280px">
                <!-- 折线图 -->
                <div ref="echarts1" style="height: 280px;"></div>
            </el-card>
            <div class="graph">
                <el-card style="height: 260px">
                    <div ref="echarts2" style="height: 260px;"></div>
                </el-card>
                <el-card style="height: 260px">
                    <div ref="echarts3" style="height: 240px;"></div>
                </el-card>
            </div>
        </el-col>
    </el-row>
</template>

<script>
import { getData } from '../api'
import * as echarts from 'echarts'
import { toRefs } from 'vue';
import { ref } from 'vue';
import { onMounted } from 'vue';
export default {
    data() {
        return {
            tableData: [],
            tableLabel: {
                name: '课程',
                todayBuy: '今日购买',
                monthBuy: '本月购买',
                totalBuy: '总购买'
            },
            countData: [
                {
                    name: "今日支付订单",
                    value: 1234,
                    icon: "success",
                    color: "#2ec7c9",
                },
                {
                    name: "今日收藏订单",
                    value: 210,
                    icon: "star-on",
                    color: "#ffb980",
                },
                {
                    name: "今日未支付订单",
                    value: 1234,
                    icon: "s-goods",
                    color: "#5ab1ef",
                },
                {
                    name: "本月支付订单",
                    value: 1234,
                    icon: "success",
                    color: "#2ec7c9",
                },
                {
                    name: "本月收藏订单",
                    value: 210,
                    icon: "star-on",
                    color: "#ffb980",
                },
                {
                    name: "本月未支付订单",
                    value: 1234,
                    icon: "s-goods",
                    color: "#5ab1ef",
                },
            ],
        }
    },
    mounted() {
        getData().then(({ data }) => {
            const { tableData } = data.data;
            this.tableData = tableData;
            //基于准备好的dom,初始化echarts实例
            const echarts1 = echarts.init(this.$refs.echarts1)
            //指定图标的配置项和数据
            var echarts1option = {}
            //处理数据xAxis-x轴
            const { orderData, userData, videoData } = data.data
            const xAxis = Object.keys(orderData.data[0])
            const xAxisData = { data: xAxis }
            echarts1option.xAxis = xAxisData;
            echarts1option.yAxis = {}
            echarts1option.legend = xAxisData;
            echarts1option.series = []
            //对原数据进行重新组装
            xAxis.forEach(key => {
                echarts1option.series.push({
                    name: key,
                    data: orderData.data.map(item => item[key]),
                    type: 'line',
                })
            })
            //使用刚指定的配置项和数据显示图表
            echarts1.setOption(echarts1option)
            //柱状图
            const echarts2 = echarts.init(this.$refs.echarts2)
            const echarts2option = {
                legend: {
                    // 图例文字颜色
                    textStyle: {
                        color: "#333",
                    },
                },
                grid: {
                    left: "20%",
                },
                // 提示框
                tooltip: {
                    trigger: "axis",
                },
                xAxis: {
                    type: "category", // 类目轴
                    data: 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: userData.map(item => item.new),
                        type: 'bar'
                    },
                    {
                        name: '活跃用户',
                        data: userData.map(item => item.active),
                        type: 'bar'
                    }
                ],
            }
            //调用实例的setOption
            echarts2.setOption(echarts2option)
            //饼状图
            const echarts3 = echarts.init(this.$refs.echarts3)
            const echarts3option = {
                tooltip: {
                    trigger: "item",
                },
                color: [
                    "#0f78f4",
                    "#dd536b",
                    "#9462e5",
                    "#a6a6a6",
                    "#e1bb22",
                    "#39c362",
                    "#3ed1cf",
                ],
                series: [
                    {
                        data: videoData,
                        type: 'pie',
                    },
                ],
            }
            echarts3.setOption(echarts3option);
        })
    }
}
</script>
<style lang="less" scoped>
.user {
    padding-bottom: 20px;
    margin-bottom: 20px;
    border-bottom: 1px solid #ccc;
    display: flex;
    align-items: center;
    img {
        margin-right: 40px;
        width: 150px;
        height: 150px;
        border-radius: 50%;
    }
    .userinfo {
        .name {
            font-size: 32px;
            margin-bottom: 10px;
        }
        .access {
            color: #999999;
        }
    }
}
.login-info {
    p {
        line-height: 28px;
        font-size: 14px;
        color: #999999;
        span {
            color: #666666;
            margin-left: 60px;
        }
    }
}
.num {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    .icon {
        width: 80px;
        height: 80px;
        font-size: 30px;
        color: #fff;
        text-align: center;
        line-height: 80px;
    }
    .detail {
        margin-left: 15px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        .price {
            padding: none;
            font-size: 30px;
            margin-top: 10px;
            margin-bottom: 10px;
            line-height: 30px;
            height: 30px;
        }
        .desc {
            font-size: 10px;
            color: #999;
            text-align: center;
        }
    }
    .el-card {
        width: 32%;
        margin-bottom: 20px;
    }
}
.graph {
    margin-top: 20px;
    display: flex;
    justify-content: space-between;
    .el-card {
        width: 48%
    }
}
</style>
  • 55
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值