vue通用后台管理系统(三)
三、Home组件
1.Home组件布局
整个页面布局分为左右两个部分------element-ui中的Layout 布局,以及自适应、鼠标移入时阴影效果的实现------element-ui中的Card卡片布局
span:表示左右侧页面所占用的比例,一共是24份
flex布局:能让相邻的两行内容在一行显示
(1)左侧部分布局--用户信息展示部分
①内容的构建--左侧上面部分
②样式的调整---左侧上面部分
.user {
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;
}
}
}
③内容的构建--左侧下面部分
④样式的调整--左侧下面部分
.user {
padding-bottom: 20px;
margin-bottom: 20px;
}
.login-info {
p {
line-height: 28px;
font-size: 14px;
color:#999999;
span {
color: #666;
margin-left: 60px;
}
}
}
(2)左侧部分布局--home购买统计部分
数据展示一般使用table、鼠标移入有高亮显示----element-ui中的Table表格组件
① 内容的构建
<el-card>
<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>
</el-card>
//表格数据
data() {
return {
tableData: [
{
name: 'oppo',
todayBuy: 100,
monthBuy: 300,
totalBuy: 800
},
{
name: 'vivo',
todayBuy: 100,
monthBuy: 300,
totalBuy: 800
},
{
name: '苹果',
todayBuy: 100,
monthBuy: 300,
totalBuy: 800
},
{
name: '小米',
todayBuy: 100,
monthBuy: 300,
totalBuy: 800
},
{
name: '三星',
todayBuy: 100,
monthBuy: 300,
totalBuy: 800
},
{
name: '魅族',
todayBuy: 100,
monthBuy: 300,
totalBuy: 800
}
]
}
}
②样式的调整
代码优化:将数据信息存为一个对象,然后进行遍历
<el-table-column v-for="(val,key) in tableLabel" :key="key" :prop="key" :label="val"/>
tableLabel: {
name: '课程',
todayBuy: '今日购买',
monthBuy: '本月购买',
totalBuy:'总购买'
}
(3)右侧部分-订单统计部分实现
该部分的整体布局也是一个左右布局--左侧是一个图标,右侧是显示的内容
① 将数据渲染到页面上
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",
},
],
② 样式的调整
<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>
<style lang="less" scoped>
.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 {
font-size: 30px;
margin-bottom: 10px;
line-height: 30px;
height: 30px;
}
.desc {
font-size: 14px;
color:#999;
text-align: center;
}
}
.el-card {
width: 32%;
margin-bottom: 20px;
}
}
</style>
2.axios
(1)axios的基本使用
axios安装:npm install axios
(2)axios的二次封装
封装的基本思路:在创建的实例上面配置一些属性,然后对外进行暴露
拦截器:可以在请求拦截器和响应拦截器里面封装一些具体的公用的方法
//axios二次封装 src/utils.request.js
import axios from "axios";
//创建实例
const http = axios.create({
//通用请求的地址前缀
baseURL: '/api',
//超时时间:最大的请求时间,单位是毫秒
timeout: 10000,
})
//拦截器是绑定在当前创建的实例http上面的
// 添加请求拦截器
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
测试请求是否可以调用成功:
首先在src/api/index.js中配置接口数据的信息,请求首页数据
//api文件夹中放置关于接口数据的信息
import http from '../utils/request'
//请求首页数据
export const getData = () => {
//返回一个Promise对象
return http.get('/home/getData')
}
然后在Home.vue组件中获取请求到的数据:
3.mock数据模拟
mock.js:生成随机数据,拦截ajax请求
下载mockjs:npm i mockjs
mock是前端用来模拟后端接口的工具库,可以通过拦截前端发起的请求,然后自己定义一些仿真的数据来实现数据的交互
① 定义mock请求拦截:
//api/mock.js
import Mock from 'mockjs'
import homeApi from './mockServeData/home'
//定义mock请求拦截
//第一个参数:请求地址 ,第二个参数:请求类型(如果是get可以省略, 第三个参数:拦截到请求后的处理函数
Mock.mock('/api/home/getData', 'get', homeApi.getStatisticalData)
② 数据封装:
// 封装模拟的首页数据
// 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
}
]
}
}
}
}
③使用封装好的数据:
注意:需要在main.js入口文件中引入mock:
import './api/mock'
4.首页可视化图表样式调整
(1)将左侧table中的数据换成动态数据
(2)调整右侧图表样式布局--flex布局
<el-card style="height: 280px;">
<!-- 折线图 -->
</el-card>
<div class="graph">
<el-card style="height: 260px;"></el-card>
<el-card style="height: 260px;"></el-card>
</div>
.graph {
margin-top: 20px;
display: flex;
justify-content: space-between;
.el-card {
width: 48%;
}
}
(3)echarts表格的基本使用
echarts安装: npm i echarts@5.1.2
(4)echarts表格的折线图
<el-card style="height: 280px;">
<!-- 折线图 -->
<div ref="echarts1" style="height: 280px;"></div>
</el-card>
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 } = 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)
})
}
.graph {
margin-top: 20px;
display: flex;
justify-content: space-between;
.el-card {
width: 48%;
}
}
------没咋懂------
(5)echarts表格的柱状图
先观察数据是一个数组----echarts必须的属性:xAxis、yAxis、series...
x轴:对数组进行遍历获得date数据
series分为两组:new和active,然后分别得到两个数组
//获取mock中的数据
const { orderData,userData } = data.data
//柱状图
//初始化实例
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)
})
(6)echarts表格的饼状图
//饼状图
//初始化实例
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'
}
],
}
//调用实例的setOption
echarts3.setOption(echarts3option)
5.顶部面包屑的实现
(1)面包屑&tag的介绍
要实现面包屑的功能:当点击商品管理时,顶部出现面包屑,同时在下面的内容区出现tag(当前的tag标签是一个高亮显示)
功能:可以通过点击面包屑实现路由的切换 (同时tag高亮显示)、点击tag时也要实现路由的切换
-----tag和面包屑需要相互联动 、数据同步(组件通信)
tag属于公共部分--main.vue中
(2)面包屑数据处理
面包屑默认显示首页,且显示在Header区;如果点击面包屑没有的路由组件话,就会增加一个面包屑;点击已有的菜单时,面包屑不会重复添加 -----在不同组件间实现数据的共享--vuex
element-ui--面包屑
点击菜单时需要获取到当前路由组件的名称和路径
(3)面包屑样式遗留问题处理
①面包屑应该与button按钮所处一行 ②一些样式调整
.l-content {
display: flex;
align-items: center; //垂直居中
/deep/.el-breadcrumb__item {
.el-breadcrumb__inner {
font-weight:normal ;
&.is-link {
color:#666;
}
}
&:last-child{
.el-breadcrumb__inner {
color:#fff
}
}
}
}
(4)tag功能介绍及页面编写
tag包含的功能:默认显示首页的tag;点击左侧菜单时会新增一个tag;点击tag时会引起页面路由的一个跳转;点击tag关闭的时候,点击的tag会被删除,同时其对应的面包屑也会删除;当前激活的tag标签会有一个高亮的效果
样式部分----elementui中的tag标签
需要判断当前的name属性是不是所需要渲染的name属性
:effect="$route.name === item.name ? 'dark':'plain'"
(5)tag点击与删除事件
①点击tag进行路由跳转:添加点击事件
② 点击tag进行路由组件的删除
首先,点击tag删除的时候是调用的store中的mutation方法实现了路径的删除
然后,获取到更新之后的数据 -1,获取到当前点击的tag的位置标识
//获取当前点击的tag的长度(索引)
const length = this.tags.length - 1;
最后,删除之后的逻辑----有点没懂