农产品市场可视化大屏前端开发完整文档
一、项目整体说明
1. 业务场景
本项目为农产品市场指标可视化大屏,完整对接 SpringBoot + MyBatis 后端服务,读取 MySQL 中 ADS 分层市场指标数据,使用前端可视化图表展示市场统计数据。 完整数据流: Hive离线计算ADS指标表 → MySQL存储指标数据 → SpringBoot接口提供JSON数据 → Vue接收数据 → ECharts渲染各类图表展示在页面
2. 完整技术栈介绍
2.1 Vue.js 框架介绍
Vue 是一套渐进式 JavaScript 前端框架,本项目使用 Vue2 版本,核心优势:
1. 数据驱动视图:无需手动操作DOM,数据变化页面自动更新;
2. 生命周期钩子:提供页面加载、更新、销毁等阶段回调,本项目使用mounted页面渲染完成后自动请求接口;
3. 代码分层管理:业务逻辑统一放入methods,结构清晰易维护;
4. 轻量无侵入,可直接嵌入页面使用,无需复杂工程化配置。
2.2 ECharts 可视化图表库介绍
ECharts 是百度开源的专业数据可视化库,适配大屏、PC、移动端,本项目用来渲染饼图、柱状图、折线图: 1. 内置丰富图表类型:饼图、柱状图、折线图、地图等开箱即用; 2. 高度自定义样式:适配深色大屏,可自定义标题、坐标轴、文字颜色、图例; 3. 自带自适应能力:监听浏览器窗口缩放,图表自动拉伸适配; 4. 支持数组数据快速映射,完美对接后端返回的JSON数组。
二、页面骨架 index.html(补充完整带详细注释)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<!-- 适配大屏,禁止缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>农产品市场指标大屏</title>
<!-- 引入全局样式文件 -->
<link rel="stylesheet" href="./index.css">
<!-- 引入外部CDN资源 -->
<!-- Vue2 核心库 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<!-- Axios 网络请求库 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.24.0/dist/axios.min.js"></script>
<!-- ECharts 图表库 -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
</head>
<body>
<!-- Vue根挂载容器,所有Vue渲染内容全部放在#app内部 -->
<div id="app">
<!-- 第一行:顶部核心指标卡片区域 -->
<div class="top-card-wrap">
<!-- 一级分类总数卡片 -->
<div class="card">
<h3>市场一级品类总数</h3>
<!-- Vue插值表达式,读取data中的index对象字段 -->
<div class="num">{{ index.totalCategoryCnt }}</div>
</div>
<!-- 二级分类总数卡片 -->
<div class="card">
<h3>市场二级品类总数</h3>
<div class="num">{{ index.secondCategoryCnt }}</div>
</div>
<!-- 市场平均价格卡片 -->
<div class="card">
<h3>全市场商品均价</h3>
<div class="num">{{ index.marketAvgPrice }}</div>
</div>
</div>
<!-- 第二行:三栏图表区域 -->
<div class="row-two">
<!-- 价格区间饼图容器 -->
<div class="chart-box" id="pie-box"></div>
<!-- 涨幅柱状图容器 -->
<div class="chart-box" id="rise-box"></div>
<!-- 跌幅柱状图容器 -->
<div class="chart-box" id="fall-box"></div>
</div>
<!-- 第三行:通栏15日价格折线图 -->
<div class="line-full-box" id="line-box"></div>
</div>
<!-- 引入Vue业务逻辑、图表渲染代码 -->
<script src="./vue_echarts.js"></script>
</body>
</html>
HTML关键注释说明
id="app":Vue实例挂载唯一根节点,Vue会接管该容器内所有DOM渲染;{{ 变量名 }}:Vue插值语法,自动读取Vue实例data中的响应式数据;- 带id的div(pie-box/rise-box等):ECharts渲染目标容器,通过id获取DOM初始化图表;
- 外部CDN引入:Vue、Axios、ECharts均通过在线CDN引入,无需本地下载依赖;
- 分层容器:按业务模块划分卡片、图表区域,配合index.css弹性布局实现大屏排版。
三、样式文件 index.css 带完整注释
/* 全局初始化:清除浏览器默认内外边距,统一盒模型计算规则 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 页面全局基础配置:深色大屏底色、白色文字、全局字体、页面边距 */
body {
background-color: #0b101a; /* 最深页面背景,深色大屏风格 */
color: #ffffff; /* 页面默认文字白色 */
padding: 20px;
font-family: "Microsoft Yahei", sans-serif;
}
/* 顶部三张指标卡片外层容器,弹性横向布局 */
.top-card-wrap {
display: flex; /* 弹性布局,子元素横向排列 */
gap: 20px; /* 卡片之间间距20px */
margin-bottom: 20px; /* 距离下方图表区域外边距 */
}
/* 单张数字卡片通用样式 */
.card {
flex: 1; /* 所有卡片自动平分父容器宽度 */
background: #151c2b; /* 卡片深色背景,区分页面底色 */
padding: 30px 10px;
text-align: center; /* 文字居中 */
border-radius: 10px; /* 圆角美化 */
border: 1px solid #243048; /* 浅色边框,增加层次感 */
}
/* 卡片标题文字样式 */
.card h3 {
font-size: 20px;
margin-bottom: 15px;
color: #c0d2e8; /* 浅蓝灰色弱化标题 */
}
/* 核心数字高亮样式,突出指标数值 */
.num {
font-size: 56px;
color: #36d8ff; /* 亮蓝色高亮数字 */
font-weight: bold;
}
/* 第二行三个图表外层容器 */
.row-two {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
/* 单个图表盒子通用样式 */
.chart-box {
flex: 1; /* 三栏等分宽度 */
height: 420px; /* 固定图表高度 */
background: #151c2b;
border-radius: 10px;
border: 1px solid #243048;
}
/* 底部通栏折线图容器,宽度铺满整行,高度更大 */
.line-full-box {
width: 100%;
height: 480px;
background: #151c2b;
border-radius: 10px;
border: 1px solid #243048;
}
四、业务逻辑 vue_echarts.js 逐行详细注释
/* global Vue, axios, echarts */
// 创建Vue根实例,统一管理页面所有数据、接口请求、图表渲染逻辑
new Vue({
// el:挂载DOM节点,绑定index.html中id="app"容器,Vue接管该区域渲染
el: "#app",
// data:Vue响应式数据仓库,存储后端返回的所有图表数据
data() {
return {
index: {}, // 顶部三张卡片指标对象(一级品类、二级品类、市场均价)
pieList: [], // 饼图数据源:价格区间统计数组
riseList: [], // 涨幅柱状图数据源:涨幅商品top5数组
fallList: [], // 跌幅柱状图数据源:跌幅商品top5数组
lineList: [] // 折线图数据源:多品类15天价格趋势数组
}
},
// mounted生命周期钩子:页面DOM渲染完成后自动执行,用于初始化加载数据
mounted() {
this.loadAllData();
},
// methods:封装所有业务方法,统一存放接口请求、图表绘制函数
methods: {
/**
* 一次性并行请求全部5个后端接口,提升页面加载速度
* async/await:异步语法,同步写法处理异步请求
*/
async loadAllData() {
try {
// Promise.all:并行发送5个GET请求,同时等待所有接口返回结果
const [indexRes, pieRes, riseRes, fallRes, lineRes] = await Promise.all([
axios.get("/api/index"), // 顶部指标接口
axios.get("/api/pie"), // 价格区间饼图接口
axios.get("/api/rise"), // 涨幅商品柱状图接口
axios.get("/api/fall"), // 跌幅商品柱状图接口
axios.get("/api/line") // 15日价格折线图接口
]);
// 将后端返回JSON数据赋值给Vue响应式变量,页面自动刷新数据
this.index = indexRes.data;
this.pieList = pieRes.data;
this.riseList = riseRes.data;
this.fallList = fallRes.data;
this.lineList = lineRes.data;
// 数据赋值完成后,依次渲染所有ECharts图表
this.drawPie();
this.drawRiseBar();
this.drawFallBar();
this.drawLineChart();
} catch (err) {
// 捕获接口异常,控制台打印错误信息,方便排查404/数据库异常
console.error("数据加载失败", err);
}
},
/**
* 绘制价格区间分布饼图
*/
drawPie() {
// 1. 根据DOM容器ID初始化ECharts图表实例
const chart = echarts.init(document.getElementById("pie-box"));
// 2. 定义图表完整配置项
const opt = {
title: { text: "全市场价值带分布", left: "center", textStyle: { color: "#fff" } },
tooltip: { trigger: "item" }, // 鼠标悬浮弹出提示框
series: [{
type: "pie", // 指定图表类型为饼图
radius: "65%", // 饼图圆环半径大小
// map遍历后端数组,转换为ECharts标准name-value格式数据
data: this.pieList.map(item => ({
name: item.priceRange, // 价格区间名称
value: item.recordCount // 该区间商品数量
}))
}]
};
// 3. 将配置项注入图表,完成渲染
chart.setOption(opt);
// 4. 监听浏览器窗口缩放,自动重绘图表,实现大屏自适应
window.addEventListener("resize", () => chart.resize());
},
/**
* 绘制当日涨幅商品柱状图
*/
drawRiseBar() {
const chart = echarts.init(document.getElementById("rise-box"));
const opt = {
title: { text: "当日价格涨幅表", left: "center", textStyle: { color: "#fff" } },
tooltip: { trigger: "axis" },
// X轴:商品名称数组
xAxis: {
data: this.riseList.map(i => i.prodName),
axisLine: { lineStyle: { color: "#556" } }, // 坐标轴线条灰色适配深色背景
axisLabel: { color: "#ddd" } // X轴文字浅白色
},
yAxis: { axisLine: { lineStyle: { color: "#556" } }, axisLabel: { color: "#ddd" } },
series: [{
type: "bar", // 柱状图类型
data: this.riseList.map(i => i.priceDiff),
itemStyle: { color: "#34d399" } // 上涨使用绿色区分
}]
};
chart.setOption(opt);
window.addEventListener("resize", () => chart.resize());
},
/**
* 绘制当日跌幅商品柱状图
*/
drawFallBar() {
const chart = echarts.init(document.getElementById("fall-box"));
const opt = {
title: { text: "当日价格跌幅表", left: "center", textStyle: { color: "#fff" } },
tooltip: { trigger: "axis" },
xAxis: {
data: this.fallList.map(i => i.prodName),
axisLine: { lineStyle: { color: "#556" } },
axisLabel: { color: "#ddd" }
},
yAxis: { axisLine: { lineStyle: { color: "#556" } }, axisLabel: { color: "#ddd" } },
series: [{
type: "bar",
data: this.fallList.map(i => i.priceDiff),
itemStyle: { color: "#f87171" } // 下跌使用红色区分
}]
};
chart.setOption(opt);
window.addEventListener("resize", () => chart.resize());
},
/**
* 绘制15日多品类均价折线图
*/
drawLineChart() {
const chart = echarts.init(document.getElementById("line-box"));
// X轴固定15个时间节点
const xAxis = ["day0","day1","day2","day3","day4","day5","day6","day7","day8","day9","day10","day11","day12","day13","day14"];
// 遍历后端多品类数据,生成多条折线配置
const series = this.lineList.map(item => ({
name: item.lineName, // 品类名称(蔬菜/水果/肉禽蛋)
type: "line", // 折线图类型
smooth: true, // 开启平滑曲线
// 提取15天价格数据作为折线数值
data: [
item.day0,item.day1,item.day2,item.day3,item.day4,
item.day5,item.day6,item.day7,item.day8,item.day9,
item.day10,item.day11,item.day12,item.day13,item.day14
]
}));
const opt = {
title: { text: "15日蔬菜、肉禽蛋、水果平均价格走势图", left: "center", textStyle: { color: "#fff" } },
tooltip: { trigger: "axis" },
legend: { textStyle: { color: "#eee" } }, // 图例文字白色
xAxis: { data: xAxis, axisLine: { lineStyle: { color: "#556" } }, axisLabel: { color: "#ddd" } },
yAxis: { axisLine: { lineStyle: { color: "#556" } }, axisLabel: { color: "#ddd" } },
series: series
};
chart.setOption(opt);
window.addEventListener("resize", () => chart.resize());
}
}
})
五、前后端完整交互全流程
- 浏览器打开
index.html,加载 Vue、Axios、ECharts、CSS 静态资源; - Vue实例执行
mounted生命周期,自动调用loadAllData; - Axios 使用
Promise.all并行请求5个后端接口; - SpringBoot
ScreenController接收请求,调用MarketMapper执行MyBatis SQL查询MySQL指标表; - SQL查询结果封装为 List/Map,自动序列化为JSON返回前端;
- Vue 将JSON数据存入
data响应式变量,页面插值自动渲染顶部数字卡片; - 依次执行绘图方法,ECharts读取Vue数据渲染饼图、涨跌柱状图、趋势折线图;
- index.css 提供深色大屏弹性布局,完成页面美化。
六、核心知识点总结
1. Vue.js 核心知识点
el:Vue实例挂载DOM容器,限定Vue渲染范围;data:响应式数据仓库,数据变化自动更新页面,无需手动操作DOM;mounted:页面DOM渲染完成后执行,适合初始化加载接口数据;methods:统一封装复用函数(接口请求、图表绘制);{{ }}插值语法:在HTML中直接读取Vue内数据展示。
2. ECharts 核心知识点
echarts.init(DOM节点):初始化图表实例,绑定页面容器;setOption(option):传入标题、坐标轴、数据、样式配置,渲染图表;chart.resize():窗口自适应,大屏必备功能;- 支持 pie/bar/line 多图表类型,可自定义文字、线条、柱状颜色适配深色大屏。
3. 性能优化亮点
Promise.all并行请求多个接口,大幅缩短页面加载耗时;- 图表方法封装复用,代码结构清晰,便于后期维护扩展;
- Flex弹性布局自动适配不同分辨率大屏,无需写多套样式;
- 前后端分离架构,数据与展示解耦,指标修改仅需调整后端SQL,前端无需改动。