一、前言
在现代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";
//引入echarts
import * 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 实现数据可视化,帮助用户更直观地理解数据趋势。通过本项目,希望大家可以一起了解前端项目开发的流程,一起学习~
标签:name,series,js,item,Vue2,new,data,ECharts,Mock From: https://blog.csdn.net/weixin_74457498/article/details/142928090