欢迎来到安国润的个人网站

农产品市场可视化大屏前端开发完整文档

返回列表

农产品市场可视化大屏前端开发完整文档

一、项目整体说明

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关键注释说明

  1. id="app":Vue实例挂载唯一根节点,Vue会接管该容器内所有DOM渲染;
  2. {{ 变量名 }}:Vue插值语法,自动读取Vue实例data中的响应式数据;
  3. 带id的div(pie-box/rise-box等):ECharts渲染目标容器,通过id获取DOM初始化图表;
  4. 外部CDN引入:Vue、Axios、ECharts均通过在线CDN引入,无需本地下载依赖;
  5. 分层容器:按业务模块划分卡片、图表区域,配合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());
        }
    }
})

五、前后端完整交互全流程

  1. 浏览器打开 index.html,加载 Vue、Axios、ECharts、CSS 静态资源;
  2. Vue实例执行mounted生命周期,自动调用loadAllData
  3. Axios 使用Promise.all并行请求5个后端接口;
  4. SpringBoot ScreenController接收请求,调用MarketMapper执行MyBatis SQL查询MySQL指标表;
  5. SQL查询结果封装为 List/Map,自动序列化为JSON返回前端;
  6. Vue 将JSON数据存入data响应式变量,页面插值自动渲染顶部数字卡片;
  7. 依次执行绘图方法,ECharts读取Vue数据渲染饼图、涨跌柱状图、趋势折线图;
  8. index.css 提供深色大屏弹性布局,完成页面美化。

六、核心知识点总结

1. Vue.js 核心知识点

  1. el:Vue实例挂载DOM容器,限定Vue渲染范围;
  2. data:响应式数据仓库,数据变化自动更新页面,无需手动操作DOM;
  3. mounted:页面DOM渲染完成后执行,适合初始化加载接口数据;
  4. methods:统一封装复用函数(接口请求、图表绘制);
  5. {{ }}插值语法:在HTML中直接读取Vue内数据展示。

2. ECharts 核心知识点

  1. echarts.init(DOM节点):初始化图表实例,绑定页面容器;
  2. setOption(option):传入标题、坐标轴、数据、样式配置,渲染图表;
  3. chart.resize():窗口自适应,大屏必备功能;
  4. 支持 pie/bar/line 多图表类型,可自定义文字、线条、柱状颜色适配深色大屏。

3. 性能优化亮点

  1. Promise.all 并行请求多个接口,大幅缩短页面加载耗时;
  2. 图表方法封装复用,代码结构清晰,便于后期维护扩展;
  3. Flex弹性布局自动适配不同分辨率大屏,无需写多套样式;
  4. 前后端分离架构,数据与展示解耦,指标修改仅需调整后端SQL,前端无需改动。