以下是静态统计图可以直接看到统计图,复制粘贴即可看到效果,但是数据是死的。下面我会介绍一种动态的方法 ,后端动态返回,基于订单页面的数据,来渲染统计图。
Vue 安装 Echarts
npm i echarts -S
静态 :
<template>
<div>
<el-row :gutter="10">
<el-col :span="12">
<el-card>
<div style="width: 100%; height: 400px" id="line"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<div style="width: 100%; height: 400px" id="bar"></div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="10" style="margin: 10px 0">
<el-col :span="12">
<el-card>
<div style="width: 100%; height: 400px" id="pie"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import * as echarts from 'echarts'
const option = {
title: {
text: '订单销售的趋势图',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
left: 'left'
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
name: '金额',
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true
},
{
name: '库存',
data: [356, 987, 457, 768, 390, 680, 1920],
type: 'line',
smooth: true
}
]
}
const option1 = {
title: {
text: '订单销售的柱状图',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
left: 'left'
},
xAxis: {
type: 'category',
data: ['水果', '零食', '饮料', '奶制品', '生活用品']
},
yAxis: {
type: 'value'
},
series: [
{
name: '金额',
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'bar',
smooth: true
},
{
name: '销量',
data: [100, 200, 204, 209, 590, 698, 700],
type: 'bar',
smooth: true
}
]
}
const option2 = {
title: {
text: '订单销售统计',
subtext: '比例图',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: 'Access From',
type: 'pie',
center: ['50%', '60%'],
radius: '50%',
data: [
{ value: 1048, name: 'Search Engine' },
{ value: 735, name: 'Direct' },
{ value: 580, name: 'Email' },
{ value: 484, name: 'Union Ads' },
{ value: 300, name: 'Video Ads' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
export default {
name: "Charts",
data() {
return {}
},
mounted() { // 等待页面的元素全部加载完成之后再初始化
// 折线图
let linetDom = document.getElementById('line');
let lineChart = echarts.init(linetDom);
lineChart.setOption(option)
// 柱状图
let barDom = document.getElementById('bar');
let barChart = echarts.init(barDom);
barChart.setOption(option1)
// 饼图
let pieDom = document.getElementById('pie');
let pieChart = echarts.init(pieDom);
pieChart.setOption(option2)
},
methods: {}
}
</script>
<style scoped>
</style>
动态:
前端:创建charts.vue 显示统计图页面
<template>
<div>
<!-- 创建一个包含两个列的行,列之间有10px的间距 -->
<el-row :gutter="10">
<!-- 左侧的列,宽度为12个栅格 -->
<el-col :span="12">
<el-card>
<!-- 用于显示折线图的容器,设置宽高 -->
<div style="width: 100%; height: 400px" id="line"></div>
</el-card>
</el-col>
<!-- 右侧的列,宽度为12个栅格 -->
<el-col :span="12">
<el-card>
<!-- 用于显示柱状图的容器,设置宽高 -->
<div style="width: 100%; height: 400px" id="bar"></div>
</el-card>
</el-col>
</el-row>
<!-- 创建一个包含一个列的行,列之间有10px的间距 -->
<el-row :gutter="10" style="margin: 10px 0">
<!-- 唯一的列,宽度为12个栅格 -->
<el-col :span="12">
<el-card>
<!-- 用于显示饼图的容器,设置宽高 -->
<div style="width: 100%; height: 400px" id="pie"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
// 导入 ECharts 库
import * as echarts from 'echarts';
// 定义折线图的配置选项
const option = {
title: {
text: '订单销售的趋势图', // 折线图的标题
left: 'center' // 标题居中
},
tooltip: {
trigger: 'axis' // 鼠标悬浮时显示的提示框,按坐标轴触发
},
legend: {
left: 'left' // 图例放置在左侧
},
xAxis: {
type: 'category', // 横轴为类目轴
data: [] // 存放横轴数据的数组
},
yAxis: {
type: 'value' // 纵轴为数值轴
},
series: [
{
name: '金额', // 系列名称
data: [], // 存放数据的数组
type: 'line', // 图表类型为折线图
smooth: true // 平滑曲线
},
]
}
// 定义柱状图的配置选项
const option1 = {
title: {
text: '订单销售的柱状图', // 柱状图的标题
left: 'center' // 标题居中
},
tooltip: {
trigger: 'axis' // 鼠标悬浮时显示的提示框,按坐标轴触发
},
legend: {
left: 'left' // 图例放置在左侧
},
xAxis: {
type: 'category', // 横轴为类目轴
data: [] // 存放横轴数据的数组
},
yAxis: {
type: 'value' // 纵轴为数值轴
},
series: [
{
name: '金额', // 系列名称
data: [], // 存放数据的数组
type: 'bar', // 图表类型为柱状图
smooth: true // 平滑曲线(对于柱状图无实际效果)
}
]
}
// 定义饼图的配置选项
const option2 = {
title: {
text: '订单销售统计', // 饼图的标题
subtext: '比例图', // 饼图的副标题
left: 'center' // 标题居中
},
tooltip: {
trigger: 'item' // 鼠标悬浮时显示的提示框,按项目触发
},
legend: {
orient: 'vertical', // 图例方向为垂直
left: 'left' // 图例放置在左侧
},
series: [
{
name: '金额', // 系列名称
type: 'pie', // 图表类型为饼图
center: ['50%', '60%'], // 饼图中心位置
radius: '50%', // 饼图半径
data: [], // 存放数据的数组
label: {
show: true, // 显示标签
formatter(param) {
return param.name + ' (' + param.percent + '%)'; // 标签格式化
}
},
emphasis: {
itemStyle: {
shadowBlur: 10, // 鼠标悬浮时的阴影模糊程度
shadowOffsetX: 0, // 鼠标悬浮时的阴影横向偏移
shadowColor: 'rgba(0, 0, 0, 0.5)' // 鼠标悬浮时的阴影颜色
}
}
}
]
}
// 导出默认的 Vue 组件
export default {
name: "Charts", // 组件名称
data() {
return {} // 返回的数据对象
},
mounted() {
// 折线图的初始化
let linetDom = document.getElementById('line'); // 获取折线图的 DOM 元素
let lineChart = echarts.init(linetDom); // 初始化 ECharts 实例
// 发送请求获取折线图的数据
this.$request.get('/echarts/linecharts').then(res => {
// 处理返回数据并设置到折线图的配置选项中
option.xAxis.data = res.data?.line?.map(v => v.date) || []; // 设置横轴数据
option.series[0].data = res.data?.line?.map(v => v.value) || []; // 设置纵轴数据
lineChart.setOption(option); // 设置折线图的配置
});
// 柱状图的初始化
let barDom = document.getElementById('bar'); // 获取柱状图的 DOM 元素
let barChart = echarts.init(barDom); // 初始化 ECharts 实例
barChart.setOption(option1); // 设置柱状图的配置
// 发送请求获取柱状图的数据
this.$request.get('/echarts/barcharts').then(res => {
// 处理返回数据并设置到柱状图的配置选项中
option1.xAxis.data = res.data?.bar?.map(v => v.name) || []; // 设置横轴数据
option1.series[0].data = res.data?.bar?.map(v => v.value) || []; // 设置纵轴数据
barChart.setOption(option1); // 设置柱状图的配置
});
// 饼图的初始化
let pieDom = document.getElementById('pie'); // 获取饼图的 DOM 元素
let pieChart = echarts.init(pieDom); // 初始化 ECharts 实例
pieChart.setOption(option2); // 设置饼图的配置
// 发送请求获取饼图的数据
this.$request.get('/echarts/piecharts').then(res => {
// 处理返回数据并设置到饼图的配置选项中
option2.series[0].data = res.data?.bar || []; // 设置数据
pieChart.setOption(option2); // 设置饼图的配置
});
},
methods: {
// 其他方法可以在这里定义
}
}
</script>
代码结构说明
-
模板部分 (
<template>
):- 使用
el-row
和el-col
组件来布局,包含三个图表:折线图、柱状图和饼图。
- 使用
-
脚本部分 (
<script>
):- 导入 ECharts 库。
- 定义不同类型图表的配置选项(折线图、柱状图和饼图)。
- 在
mounted
钩子中初始化图表,并通过 API 请求填充数据。 - 使用
this.$request.get
发送请求获取数据,并将数据更新到图表配置中。
后端:创建EchartsConteoller 前端页面的数据来源
@RestController // 标识该类为控制器,处理 HTTP 请求
@RequestMapping("/echarts") // 定义请求路径的前缀为 /echarts
public class EchartsController {
@Resource // 自动注入 OrdersService 服务
OrdersService ordersService;
/**
* 获取折线图的数据
* @return 返回包含折线图数据的结果
*/
@GetMapping("/linecharts") // 定义 GET 请求,路径为 /linecharts
public Result LineCharts() {
List<Orders> list = ordersService.list(); // 从服务中获取所有订单数据
Set<String> dates = list.stream().map(Orders::getDate).collect(Collectors.toSet()); // 获取所有日期
List<String> dateList = CollUtil.newArrayList(dates); // 将日期集合转为列表
dateList.sort(Comparator.naturalOrder()); // 对日期进行自然顺序排序
List<Dict> lineList = new ArrayList<>(); // 创建用于存放折线图数据的列表
// 遍历所有日期,计算每个日期的订单总金额
for (String date : dateList) {
// 统计当前日期的所有金额总和
BigDecimal sum = list.stream().filter(orders -> orders.getDate().equals(date)).map(Orders::getMoney)
.reduce(BigDecimal::add).orElse(BigDecimal.ZERO); // 计算总金额
Dict dict = Dict.create(); // 创建一个字典对象
Dict line = dict.set("date", date).set("value", sum); // 将日期和总金额存入字典
lineList.add(line); // 将字典添加到折线图数据列表中
}
Dict res = Dict.create().set("line", lineList); // 创建结果字典,包含折线图数据
return Result.success(res); // 返回成功的结果
}
/**
* 获取柱状图的数据
* @return 返回包含柱状图数据的结果
*/
@GetMapping("/barcharts") // 定义 GET 请求,路径为 /barcharts
public Result barCharts() {
List<Orders> list = ordersService.list(); // 从服务中获取所有订单数据
Set<String> dates = list.stream().map(Orders::getDate).collect(Collectors.toSet()); // 获取所有日期
List<String> dateList = CollUtil.newArrayList(dates); // 将日期集合转为列表
dateList.sort(Comparator.naturalOrder()); // 对日期进行自然顺序排序
List<Dict> barList = new ArrayList<>(); // 创建用于存放柱状图数据的列表
Set<String> categories = list.stream().map(Orders::getCategory).collect(Collectors.toSet()); // 获取所有分类
// 遍历所有分类,计算每个分类的订单总金额
for (String cate : categories) {
// 统计当前分类的所有金额总和
BigDecimal sum = list.stream().filter(orders -> orders.getCategory().equals(cate)).map(Orders::getMoney)
.reduce(BigDecimal::add).orElse(BigDecimal.ZERO); // 计算总金额
Dict dict = Dict.create(); // 创建一个字典对象
Dict bar = dict.set("name", cate).set("value", sum); // 将分类和总金额存入字典
barList.add(bar); // 将字典添加到柱状图数据列表中
}
Dict res = Dict.create().set("bar", barList); // 创建结果字典,包含柱状图数据
return Result.success(res); // 返回成功的结果
}
/**
* 获取饼图的数据
* @return 返回包含饼图数据的结果
*/
@GetMapping("/piecharts") // 定义 GET 请求,路径为 /piecharts
public Result pieCharts() {
List<Orders> list = ordersService.list(); // 从服务中获取所有订单数据
Set<String> dates = list.stream().map(Orders::getDate).collect(Collectors.toSet()); // 获取所有日期
List<String> dateList = CollUtil.newArrayList(dates); // 将日期集合转为列表
dateList.sort(Comparator.naturalOrder()); // 对日期进行自然顺序排序
List<Dict> barList = new ArrayList<>(); // 创建用于存放饼图数据的列表
Set<String> categories = list.stream().map(Orders::getCategory).collect(Collectors.toSet()); // 获取所有分类
// 遍历所有分类,计算每个分类的订单总金额
for (String cate : categories) {
// 统计当前分类的所有金额总和
BigDecimal sum = list.stream().filter(orders -> orders.getCategory().equals(cate)).map(Orders::getMoney)
.reduce(BigDecimal::add).orElse(BigDecimal.ZERO); // 计算总金额
Dict dict = Dict.create(); // 创建一个字典对象
Dict bar = dict.set("name", cate).set("value", sum); // 将分类和总金额存入字典
barList.add(bar); // 将字典添加到饼图数据列表中
}
Dict res = Dict.create().set("bar", barList); // 创建结果字典,包含饼图数据
return Result.success(res); // 返回成功的结果
}
}
主要功能
- 折线图数据接口:计算每个日期的订单总金额,并以字典形式返回数据。
- 柱状图数据接口:计算每个分类的订单总金额,并以字典形式返回数据。
- 饼图数据接口:与柱状图接口相同,计算每个分类的订单总金额。
这些接口为前端的 Echarts 图表提供了所需的数据支持。以上为
订单管理页面
可以设计一个订单管理页面,用来对数据进行增删改查,并可以把数据返回给统计图,实时渲染我们想要的图表,这里我直接贴代码。
前端Orders.vue
<template>
<div>
<div>
<el-input style="width: 200px" placeholder="订单名称查询" v-model="name"></el-input>
<el-input style="width: 200px" placeholder="购买人查询" v-model="userid"></el-input>
<el-button type="primary" style="margin-left: 10px" @click="load(1)">查询</el-button>
<el-button type="info" @click="reset">重置</el-button>
</div>
<div style="margin: 10px 0">
<el-button type="primary" plain @click="handleAdd">新增</el-button>
<el-button type="danger" plain @click="delBatch">批量删除</el-button>
</div>
<el-table :data="tableData" stripe :header-cell-style="{ backgroundColor: 'aliceblue', color: '#666' }" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="id" label="序号" width="70" align="center"></el-table-column>
<el-table-column prop="no" label="订单编号"></el-table-column>
<el-table-column prop="name" label="订单名称"></el-table-column>
<el-table-column prop="money" label="订单金额" show-overflow-tooltip></el-table-column>
<el-table-column prop="user" label="用户"></el-table-column>
<el-table-column prop="date" label="创建时间"></el-table-column>
<el-table-column label="操作" align="center" width="180">
<template v-slot="scope">
<el-button size="mini" type="primary" plain @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" plain @click="del(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin: 10px 0">
<el-pagination
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
layout="total, prev, pager, next"
:total="total">
</el-pagination>
</div>
<el-dialog title="信息" :visible.sync="fromVisible" width="40%" :close-on-click-modal="false">
<el-form :model="form" label-width="80px" style="padding-right: 20px" :rules="rules" ref="formRef">
<el-form-item label="订单名称" prop="name">
<el-input v-model="form.name" placeholder="名称"></el-input>
</el-form-item>
<el-form-item label="订单金额" prop="money">
<el-input v-model="form.money" placeholder="金额"></el-input>
</el-form-item>
<el-form-item label="分类" prop="category">
<el-select style="width: 100%" v-model="form.category">
<el-option v-for="item in ['水果', '蔬菜', '零食', '饮料', '奶制品', '糕点']" :key="item" :value="item"></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="fromVisible = false">取 消</el-button>
<el-button type="primary" @click="save">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default{
name:'Orders',
data(){
return{
tableData:[],
user: JSON.parse(localStorage.getItem('userToken') || '{}'),
pageNum: 1,
pageSize: 10,
username: '',
name: '',
userid:'',
total: 0,
fromVisible: false,
form: {},
ids: [],
content: '',
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
],
money: [
{ required: true, message: '请输入金额', trigger: 'blur' },
],
category: [
{ required: true, message: '请输入分类', trigger: 'blur' },
]
},
}
},
created() {
this.load()
},
methods:{
handleEdit(row) {
this.form = JSON.parse(JSON.stringify(row))
this.fromVisible = true
},
del(id) {
this.$confirm('您确认删除吗?', '确认删除', {type: "warning"}).then(response => {
this.$request.delete('/orders/delete/' + id).then(res => {
if (res.code === '200') { // 表示操作成功
this.$message.success('操作成功')
this.load(1)
} else {
this.$message.error(res.msg) // 弹出错误的信息
}})
}).catch(() => {})
},
delBatch(){
if (!this.ids.length) {
this.$message.warning('请选择数据')
return
}
this.$confirm('您确认批量删除这些数据吗?', '确认删除', {type: "warning"}).then(response => {
this.$request.delete('/orders/delete/batch', { data: this.ids }).then(res => {
if (res.code === '200') { // 表示操作成功
this.$message.success('操作成功')
this.load(1)
} else {
this.$message.error(res.msg) // 弹出错误的信息
}
})
}).catch(() => {})
},
handleSelectionChange(rows) { // 当前选中的所有的行数据
this.ids = rows.map(v => v.id)
},
save(){
// 保存按钮触发的逻辑 它会触发新增或者更新
this.$refs.formRef.validate((valid) =>{
if(valid){ this.sendSaveRequest() }
})
},
sendSaveRequest(){
this.$request({
url:this.form.id ? 'orders/update': '/orders/add',
method: this.form.id ? 'PUT' : 'POST',
data : this.form
}).then(res =>{
if(res.code === '200'){
this.$message.success('保存成功')
this.load(1)
this.fromVisible = false
}else{
this.$message.error(res.msg)
}
})
},
handleAdd(){
this.form = {} // 新增数据的时候清空数据
this.fromVisible = true // 打开弹窗
},
reset() {
this.name = ''
this.load()
},
load(pageNum){
if (pageNum) this.pageNum = pageNum
this.$request.get('/orders/selectByPage',{
params:{
pageNum: this.pageNum,
pageSize: this.pageSize,
name: this.name
}
}).then(res =>{
this.tableData = res.data.records
this.total = res.data.total
})},
handleCurrentChange(pageNum) {
this.load(pageNum)
},
},
}
</script>
<style scoped>
</style>
后端OrdersController
@RestController
@RequestMapping("/orders")
public class OrdersController {
@Autowired
OrdersService ordersService;
@Autowired
UserService userService;
/**
* 新增订单
* @return
*/
@PostMapping("add")
public Result add(@RequestBody Orders orders){
User currentUser = TokenUtils.getCurrentUser();
orders.setUserid(currentUser.getId());
orders.setDate(DateUtil.today());
orders.setNo(IdUtil.fastSimpleUUID());
ordersService.save(orders);
return Result.success();
}
/**
* 修改信息
*/
@PutMapping("/update")
public Result update(@RequestBody Orders orders) {
ordersService.updateById(orders);
return Result.success();
}
/**
* 删除信息
* @param id
* @return
*/
@DeleteMapping("/delete/{id}")
public Result delete(@PathVariable Integer id) {
ordersService.removeById(id);
return Result.success();
}
/**
* 批量删除信息
* @param ids
* @return
*/
@DeleteMapping("/delete/batch")
public Result batchDelete(@RequestBody List<Integer> ids) {
ordersService.removeByIds(ids);
return Result.success();
}
/**
* 查询全部信息
*/
@GetMapping("/selectAll")
public Result selectAll() {
List<Orders> ordersList = ordersService.list(new QueryWrapper<Orders>().orderByDesc("id"));
return Result.success(ordersList);
}
/**
* 分页查询
* @param pageNum
* @param pageSize
* @param name
* @return
*/
@GetMapping("/selectByPage")
public Result selectByPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String name) {
QueryWrapper<Orders> queryWrapper = new QueryWrapper<Orders>().orderByDesc("id");
queryWrapper.like(StrUtil.isNotBlank(name),"name",name);
Page<Orders> page = ordersService.page(new Page<>(pageNum, pageSize), queryWrapper);
List<Orders> records = page.getRecords();
for(Orders record : records){
Integer authorid = record.getUserid();
User user = userService.getById(authorid);
if(user != null){
record.setUser(user.getUsername());
}
}
return Result.success(page);
}
}
订单数据库代码
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`no` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单编号',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单名称',
`money` decimal(10,2) DEFAULT NULL COMMENT '订单金额',
`userid` int(11) DEFAULT NULL COMMENT '用户ID',
`category` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单分类',
`date` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表';