当前位置:首页 » 《我的小黑屋》 » 正文

Vue2+ECharts+Mock.js实现前端后台通用管理系统页面

9 人参与  2024年10月30日 14:40  分类 : 《我的小黑屋》  评论

点击全文阅读


一、前言

    在现代Web开发中,前端框架与数据可视化工具的结合能够显著提升用户体验。本文将介绍如何使用Vue2和ECharts构建一个通用的后台管理系统页面。利用Vue2的组件化特性,可以高效管理应用状态与UI交互,而ECharts则提供多样的图表类型,便于展示数据分析结果。通过整合这两者,开发者能够快速构建出既美观又功能强大的后台管理系统,实现数据的动态展示与实时更新,从而帮助用户做出更明智的决策。

二、技术栈

Vue2+Vuex+Echarts+Element-ui+Axios+Mock.js

Vue.js:前端框架,用于构建用户界面。Vue Router:用于页面路由管理。Vuex:状态管理库,用于管理应用的共享状态。ECharts:用于数据可视化展示。Mock.js:用于模拟后端接口数据。

三、页面区域结构

   页面整体分为三个区域,CommandAside区域、CommandHeader区域、HomePage区域,HomePage区域又分为左侧数据和右侧图标区域。图表区域又分为折线图、饼状图、柱状图区域。

1.MainPage代码实现

<el-container>: Element UI 提供的布局容器,用于构建页面的整体结构。

<el-aside>: 侧边栏区域,这里包含了 CommonAside 组件,通常用于放置导航或菜单。

<el-header>: 页头区域,这里包含了 CommonHeader 组件,通常用于显示标题或工具条。

<el-main>: 主内容区域,这里包含 <router-view>,这是 Vue Router 的占位符,用于渲染匹配的子路由组件。

<template>    <el-container>        <el-aside width="auto">            <common-aside />        </el-aside>        <el-container>            <!--使用小驼峰命名法  -->            <el-header>                <common-header />            </el-header>            <el-main>                <!-- 二级路由出口 -->                <router-view></router-view>            </el-main>        </el-container>    </el-container></template><!-- 样式区域 --><style lang="less">.el-header {    background-color: #333;}</style><script>import CommonAside from '@/components/CommonAside.vue'import CommonHeader from '@/components/CommonHeader.vue'export default {    name: 'MainPage',    data() {        return {        }    },    components: {        CommonAside,        CommonHeader    }}</script>

2.HomePage首页搭建

整体布局:使用 <el-row> 和 <el-col> 来构建响应式布局。:gutter="20" 指定了列之间的间隔为 20 像素。

左侧边栏区域 (<el-col :span="8">):

包含两个主要部分:用户信息和一个表格。

用户信息:使用 <el-card> 组件来显示用户头像和基本信息(用户名及角色)。显示上次登录时间和地点。

表格:另一个 <el-card> 组件中嵌入了 <el-table>,通过 v-for 动态生成表格的列。

右侧区域 (<el-col :span="16">):

包含统计数据和图表。

统计信息:使用 v-for 迭代 countData 数组生成多个 <el-card>,每个卡片展示一个统计项(如图标、价格和描述)。

折线图区域:一个 <el-card> 用于显示折线图,通过 ref="echart" 以便后续在 JavaScript 中引用。

饼状图区域:一个包含两个 <el-card> 的容器,用于显示不同的图表,分别通过 ref 引用。

<template>    <el-row class="home" :gutter="20">        <!-- 左侧边栏区域 gutter="20" 表示左右两列之间的间隔为20像素。:offset="8" 表示该列向右偏移8个单位-->        <el-col :span="8">            <el-card shadow=" hover">                <!-- 用户信息头像区域 -->                <div class="user">                    <!-- <img :src="getImageUrl('user')" class="user" /> -->                    <img src="../assets/images/user.png" alt="">                    <div class="user-info">                        <p class="name">Admin</p>                        <p class="access">超级管理员</p>                    </div>                </div>                <!-- 登录信息区域布局 -->                <div class="login-info">                    <p>上次登录时间:<span>2022-7-11</span></p>                    <p>上次登录的地点:<span>北京</span></p>                </div>            </el-card>            <!-- 底部卡片区域  shadow="hover" 应用到一个元素上时,该元素在鼠标悬停时会显示特定的阴影-->            <el-card style="margin-top: 20px; height: 410px;" shadow="hover" class="table">                <el-table :data="tableData">                    <el-table-column v-for="(val, key) in tableLabel" :key="key" :prop="key" :label="val">                    </el-table-column>                </el-table>            </el-card>        </el-col>        <!-- 这是右侧区域 -->        <el-col :span="16">            <div class=" num">                <el-card v-for="item in countData" :key="item.name" :body-style="{ display: 'flex', padding: 0 }        ">                    <i class="icons" :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;" class="top-echart">                <!--折线图区域  -->                <div ref="echart" style="height: 280px;"></div>            </el-card>            <!-- 右侧底部饼状图区域 -->            <div class="graph">                <el-card style="height: 210px;">                    <div ref="userEchart" style="height: 240px"></div>                </el-card>                <el-card style="height: 210px;">                    <div ref="videoEchart" style="height: 240px"></div>                </el-card>            </div>        </el-col>    </el-row></template>

3.HomePage首页中在mounted()中进行数据渲染逻辑实现

<script>import { getHomeData } from "@/api";//引入echartsimport * as echarts from "echarts";export default {    name: 'HomePage',    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: "goods",                        color: "#5ab1ef",                    },                    {                        name: "本月支付订单",                        value: 1234,                        icon: "success",                        color: "#2ec7c9",                    },                    {                        name: "本月收藏订单",                        value: 210,                        icon: "star-on",                        color: "#ffb980",                    },                    {                        name: "本月未支付订单",                        value: 1234,                        icon: "goods",                        color: "#5ab1ef",                    },                ],            observer: null,            xOptions: {                // 图例文字颜色                textStyle: {                    color: "#333",                },                legend: {},                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: [],            },            pieOptions: {                tooltip: {                    trigger: "item",                },                legend: {},                color: [                    "#0f78f4",                    "#dd536b",                    "#9462e5",                    "#a6a6a6",                    "#e1bb22",                    "#39c362",                    "#3ed1cf",                ],                series: []            },        }    },    methods: {        getImageUrl(user) {            return new URL(`../assets/images/${user}.png`, import.meta.url).href;        }    },    mounted() {        getHomeData().then(async ({ data }) => {            const { tableData } = data.data;            console.log(tableData);            this.tableData = tableData;            // 在当前位置进行echarsts,初始化echarts实            // this.getChartData();            const { orderData, userData, videoData } = data.data;            console.log(111);            console.log(orderData, userData, videoData);            // 对第一个图表的xAxis和series赋值            this.xOptions.xAxis.data = orderData.date;            this.xOptions.series = Object.keys(orderData.data[0]).map(val => ({                name: val,                data: orderData.data.map(item => item[val]),                type: "line"            }));            // one            const OneEcharts = echarts.init(this.$refs["echart"]);            OneEcharts.setOption(this.xOptions);            // 对第二个图表的xAxis和series赋值            this.xOptions.xAxis.data = userData.map(item => item.date);            this.xOptions.series = [                {                    name: "新增用户",                    data: userData.map(item => item.new),                    type: "bar",                },                {                    name: "活跃用户",                    data: userData.map(item => item.active),                    type: "bar",                }            ];            // two            const TwoEcharts = echarts.init(this.$refs["userEchart"]);            TwoEcharts.setOption(this.xOptions);            // 对第三个图表的series赋值            this.pieOptions.series = [                {                    data: videoData,                    type: "pie",                },            ];            // three            const ThreeEcharts = echarts.init(this.$refs["videoEchart"]);            ThreeEcharts.setOption(this.pieOptions);            // ResizeObserver 如果监视的容器大小变化,如果改变会执行传递的回调            this.observer = new ResizeObserver(entries => {                OneEcharts.resize();                TwoEcharts.resize();                ThreeEcharts.resize();            });            // 如果这个容器存在            if (this.$refs["echart"]) {                // 则调用监视器的observe方法,监视这个容器的大小                this.observer.observe(this.$refs["echart"]);            }        })    }}</script>

四、使用mock.js模拟后端接口数据

1.安装mock.js

npm i mockjs

2.创建mockData,新建home.js文件储存HomePage数据

      如何创建 mock 数据文件,并在其中定义 HomePage 所需的数据结构。

// 首页数组//用户的数组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: 200,            data: {                // 左侧底部card数据                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,                    },                ],                // 饼图                videoData: [                    { name: "小米", value: 2999 },                    { name: "苹果", value: 5999 },                    { name: "vivo", value: 1500 },                    { name: "oppo", value: 1999 },                    { name: "魅族", value: 2200 },                    { name: "三星", value: 4500 },                ],                // 柱状图                orderData: {                    date: [                        "2019-10-01",                        "2019-10-02",                        "2019-10-03",                        "2019-10-04",                        "2019-10-05",                        "2019-10-06",                        "2019-10-07",                    ],                    data: [                        {                            苹果: 3839,                            小米: 1423,                            华为: 4965,                            oppo: 3334,                            vivo: 2820,                            一加: 4751,                        },                        {                            苹果: 3560,                            小米: 2099,                            华为: 3192,                            oppo: 4210,                            vivo: 1283,                            一加: 1613,                        },                        {                            苹果: 1864,                            小米: 4598,                            华为: 4202,                            oppo: 4377,                            vivo: 4123,                            一加: 4750,                        },                        {                            苹果: 2634,                            小米: 1458,                            华为: 4155,                            oppo: 2847,                            vivo: 2551,                            一加: 1733,                        },                        {                            苹果: 3622,                            小米: 3990,                            华为: 2860,                            oppo: 3870,                            vivo: 1852,                            一加: 1712,                        },                        {                            苹果: 2004,                            小米: 1864,                            华为: 1395,                            oppo: 1315,                            vivo: 4051,                            一加: 2293,                        },                        {                            苹果: 3797,                            小米: 3936,                            华为: 3642,                            oppo: 4408,                            vivo: 3374,                            一加: 3874,                        },                    ],                },                // 折线图                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 },                ]            },        }    }}

3. 创建Mock.js创建后端请求接口

import Mock from 'mockjs'//引入数据import homeApi from './mockServeData/home'Mock.mock('/api/home/getData', 'get', homeApi.getStatisticalData)

4.页面中引入使用发起异步请求

import { getHomeData } from "@/api";
  mounted() {        getHomeData().then(async ({ data }) => {            const { tableData } = data.data;            console.log(tableData);            this.tableData = tableData;            // 在当前位置进行echarsts,初始化echarts实            // this.getChartData();            const { orderData, userData, videoData } = data.data;            console.log(111);            console.log(orderData, userData, videoData);            // 对第一个图表的xAxis和series赋值            this.xOptions.xAxis.data = orderData.date;            this.xOptions.series = Object.keys(orderData.data[0]).map(val => ({                name: val,                data: orderData.data.map(item => item[val]),                type: "line"            }));            // one            const OneEcharts = echarts.init(this.$refs["echart"]);            OneEcharts.setOption(this.xOptions);            // 对第二个图表的xAxis和series赋值            this.xOptions.xAxis.data = userData.map(item => item.date);            this.xOptions.series = [                {                    name: "新增用户",                    data: userData.map(item => item.new),                    type: "bar",                },                {                    name: "活跃用户",                    data: userData.map(item => item.active),                    type: "bar",                }            ];            // two            const TwoEcharts = echarts.init(this.$refs["userEchart"]);            TwoEcharts.setOption(this.xOptions);            // 对第三个图表的series赋值            this.pieOptions.series = [                {                    data: videoData,                    type: "pie",                },            ];            // three            const ThreeEcharts = echarts.init(this.$refs["videoEchart"]);            ThreeEcharts.setOption(this.pieOptions);            // ResizeObserver 如果监视的容器大小变化,如果改变会执行传递的回调            this.observer = new ResizeObserver(entries => {                OneEcharts.resize();                TwoEcharts.resize();                ThreeEcharts.resize();            });            // 如果这个容器存在            if (this.$refs["echart"]) {                // 则调用监视器的observe方法,监视这个容器的大小                this.observer.observe(this.$refs["echart"]);            }        })

 五、使用Vuex进行状态管理

1.安装Vuex

npm i vuex

2.main.js中挂载Vuex

// Vue.use(VueRouter)new Vue({  render: h => h(App),  router, // 将 router 注入到根实例  store}).$mount('#app')

 3.在index.js创建Vuex实例

import Vue from 'vue'import Vuex from 'vuex'import tab from './tab'Vue.use(Vuex)//创建vuex的实例export default new Vuex.Store({    state: {    },    modules: {        tab    }})

4.示例tab.js导出 

      提供 tab.js 的示例代码,展示如何管理选项卡的状态。

export default {    state: {        isCollapse: false//控制菜单的展开还是收齐    },    mutations: {        // 定义菜单收齐的方法        collapseMenu(state) {            state.isCollapse = !state.isCollapse        }    }}

5.CommandHeader组件中使用,头部菜单栏折叠与显示

    在 CommandHeader 组件中使用 Vuex 管理菜单栏的显示与隐藏。

​  computed: {        //没有子菜单        noChilden() {            return this.menuData.filter(item => !item.children)        },        hasChilden() {            return this.menuData.filter(item => item.children)        },        // 控制菜单栏的收起与折叠        isCollapse() {            return this.$store.state.tab.isCollapse        }    }​

六、完整页面实现

七、代码仓库地址

https://gitee.com/tanzero/back-office.git 后续还在更新中~

八、参考视频

30-vue通用后台管理(面包屑&tag介绍)_哔哩哔哩_bilibilivue项目实战,vue3项目实战,vue2+element-ui项目,vue3项目实战(已完结)_哔哩哔哩_bilibili30-vue通用后台管理(面包屑&tag介绍)_哔哩哔哩_bilibili

九、总结 

    本项目致力于使用Vue2+Vuex+Echarts+Element-ui+Axios+Mock.js构建一个功能强大的后台管理系统,旨在提升用户体验和开发效率。通过引入 Vue Router,项目能够实现灵活的页面路由管理,使得不同功能模块的切换更加顺畅。同时,使用 Vuex 进行全局状态管理,可以有效地处理应用中的共享数据,确保各个组件之间的数据一致性。为了方便前端开发,利用 Mock.js 模拟后端接口数据,允许开发者在没有真实后端服务的情况下,依然能够进行数据渲染与交互测试。首页设计采用组件化结构,通过 ECharts 实现数据可视化,帮助用户更直观地理解数据趋势。通过本项目,希望大家可以一起了解前端项目开发的流程,一起学习~

    


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/180015.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1