简介:这是一份基于若依前后端分离框架整合数据大屏与地图能力的完整示例工程,面向需要快速搭建可视化看板、地图检索类功能的Java全栈开发者,可直接嵌入现有若依项目使用,主要适配MySQL数据库。压缩包共655个文件,涵盖252个Java后台逻辑、119个Vue前端页面、115个SVG地图素材、77个JS脚本等类型,同时包含SQL初始化脚本、YML/Properties配置、启动脚本与使用说明,整体仅4.13MB,结构清晰便于按模块学习。资源重点展示了动态切换多数据源并以数据库保存配置的实现方式,提供热力图、区域检索、行政区划、线路规划、点聚合图等多种地图示例,以及大屏数据展示和仅支持MySQL的SQL在线编辑器,能够帮助理解地图接入、数据源路由和可视化集成等常见场景。目前已有2609人学习下载,适合需要基于若依体系落地大屏地图功能或研究多数据源切换方案的开发者参考借鉴。 如果一个项目已经跑在若依前后端分离框架上,现在告诉你:要加一块带地图热力图、区域分布、3D 效果和检索定位的数据大屏,你会不会下意识想另起炉灶?我之前也差点这么做,直到真正拆完需求才发现——大屏和若依根本不必是两套系统。若依虽然定位是后台管理,但路由、菜单、权限、接口封装都是开放可扩展的,完全可以在同一个工程里生长出一块独立的大屏页面,用户用浏览器打开 /screen 就能全屏展示,代码还能继续复用原来的用户体系和数据查询链路。这篇文章记录的,就是我从零到一把这套能力落到若依前后端分离项目上的完整过程,包含选型原因、核心代码、踩坑修复三个层面,适合正在做若依集成地图可视化或者打算给项目加一块大屏的朋友直接参考。
1. 需求拆解与技术选型:数据大屏为什么直接长在若依上
1.1 先搞清楚这块屏幕到底要解决什么问题
做开发最怕上来就写代码,尤其是大屏项目。实际接到需求时,客户往往说得比较含糊,既要体现整体经营情况,又要能看空间分布,最好还能搜索定位。如果一上来就去调地图 API、堆图表组件,很容易做出一堆自嗨却不好用的界面。
我当时把需求拆成了四个明确维度:
- 总览指标:订单量、销售额、设备在线数等,用常规图表展示;
- 空间分布:按省、市汇总统计值,用区域着色图呈现;
- 热点情况:面向具体经纬度点位,用热力图表现密度;
- 检索定位:输入名称或坐标后,地图可以高亮跳转。
这四个需求跟若依原有的后台管理功能完全是两个方向,但背后的数据源基本一致。比如页面里要展示的销售数据,本来就在若依的业务表里;区域分布和热点坐标,也只是在原有数据基础上多聚合一个字段而已。这就为“在若依项目里直接扩展”打下了基础,不必非得重新造一套数据体系。
1.2 为什么不在外面单独搭一套大屏系统
团队内部聊过很多方案,有人说直接用可视化平台搭,有人说单独建一个 React 项目。这些方案都有各自的优势,但放到已经跑在若依上的项目里,反而不太合适。
一个核心原因是权限体系。若依的用户、部门、菜单、角色都已经按公司制度配好了,如果大屏独立成新项目,首先就得解决单点登录和数据权限打通的问题,这部分的开发成本比大屏本身还高。另一个原因是工程复用。若依前后端分离版已经封装了统一返回结构、请求拦截、异常处理和代码生成,我只需要在原来的工程里新增几个 screen 开头的接口,前端继续用@/utils/request这些现成工具,就能把整个数据链路串起来。
所以我的结论是:不改动若依的主框架,只在原有工程里新增一个全屏路由和一组统计接口。图表库选择 ECharts 5 + echarts-gl,地图数据用 GeoJSON 静态文件放在前端目录,整体架构不变,却能多出一块独立的大屏能力。
1.3 技术选型的横向对比
| 对比项 | 选择 | 理由 |
|---|---|---|
| 前端工程 | 若依自带 Vue 项目 | 路由、权限、请求、登录态全部复用 |
| 图表库 | ECharts 5 + echarts-gl | 社区活跃,2D/3D 都覆盖,学习成本低 |
| 地图数据 | GeoJSON 静态文件 | 离线可跑,不依赖第三方在线服务稳定性 |
| 热力图实现 | ECharts geo 坐标系 | 与区域图共用一套数据结构,维护简单 |
| 在线地图引擎 | 高德/Leaflet | 按需接入,只补足路况、地形等场景 |
如果你有用 Leaflet 或者高德做底图的需求,也可以走混合方案,只是要注意坐标系换算,GeoJSON 一般用 WGS84,高德默认是 GCJ02,坐标不做偏移处理容易全部错位。这个后面单独说。
2. 前端改造:把大屏页面从管理后台的布局里“摘”出来
2.1 若依路由和布局的关系
使用若依前端时,绝大多数页面都嵌套在Layout组件里,Layout内部会渲染侧边栏、导航栏、标签页和内容区。如果我把大屏组件当作一个普通页面挂到Layout下面,打开之后四周还是会多出一圈后台框架,这显然不是数据大屏想要的效果。
要解决这个问题,需要注册一个不经过 Layout 的路由,让大屏组件直接占据整个浏览器视口。在若依的src/router/index.js中,路由分为固定路由和动态路由,菜单大多由后端动态返回,但大屏没有必要进菜单,所以可以直接放到固定路由部分。
2.2 注册一个全屏路由
我当时在constantRouterMap里加了一段配置:
{ path: '/screen', name: 'Screen', component: () => import('@/views/screen/index.vue'), hidden: true, meta: { title: '数据大屏', noCache: true } }这段配置有两个关键点:第一,component直接指向大屏组件,而不是() => import('@/layout/index.vue')的子组件;第二,hidden: true让该路由不出现在侧边栏菜单里。调试阶段我直接访问/screen,上线后给用户一个入口按钮,用window.open('/screen')打开新标签页,效果干净直接。
如果你用的是若依 Vue3 版本,还要留意AppMain.vue里的<transition>过渡动画。个别版本在首次路由跳转时会因为页面切换动画导致地图容器尺寸检测异常,表现是地图只画出半个画布。常见处理办法是把大屏路由的meta.noCache设成true,或者在AppMain.vue里判断一下路由元信息,给大屏路由跳过过渡包裹层。
2.3 菜单隐藏不等于权限放开
这里必须多说一句:hidden: true只表示菜单栏里看不到,不代表任何人都能访问。前端路由只要知道 URL 就能打开页面,所以后端接口仍然要做权限校验。若依的鉴权链路是现成的,我给大屏接口加了权限标识,只授予有需要的角色,避免出现“有人拿到地址就能看到整块大屏数据”的问题。
3. 热力图与区域图的完整落地:GeoJSON、ECharts 配置与若依接口对接
3.1 地图地理数据的准备与注册
ECharts 5 不再内置全国地图 GeoJSON,需要自己准备地理数据。我一般从公开的行政区划 GeoJSON 资源里下载,演示项目也可以直接拿市级、省级的 JSON 文件放到src/assets/geo/目录下。使用前先注册地图:
import * as echarts from 'echarts' import chinaJson from '@/assets/geo/china.json' echarts.registerMap('china', chinaJson)需要注意一个容易踩的细节:GeoJSON 里的坐标系绝大多数是 WGS84,前端直接用 ECharts 展示没问题,但如果后续要把坐标传给高德这类国内地图服务,要考虑 GCJ02 偏移问题,否则点位会整体偏离一段距离。
3.2 区域图配置细节与常见误区
区域图的核心逻辑很简单:各省份或区域名称和统计值组成{ name: '北京市', value: 320 }格式的数组,再用visualMap映射颜色区间:
const option = { tooltip: { trigger: 'item' }, visualMap: { min: 0, max: 1000, left: 20, bottom: 20, text: ['高', '低'], inRange: { color: ['#e6f2ff', '#3b7dd8', '#0b1f4d'] } }, series: [ { type: 'map', map: 'china', roam: true, label: { show: false }, data: regionData.value } ] } chart.setOption(option)这里两个容易忽略的坑:一是roam,如果不设置,默认关闭拖拽缩放,但我建议在大屏上显式关掉,避免值守人员不小心拖动后画面错位;二是visualMap的max值如果写死,数据一旦超过最大值颜色就会失真,最好的做法是后端返回一个统计好的最大值,或者前端动态计算再写入配置。
3.3 地图热力图实现与坐标顺序
地图热力图有两种常见写法,很多人容易混淆。一种是 ECharts 原生的heatmap系列,配合coordinateSystem: 'geo';另一种是用scatter或effectScatter叠加半透明圆点,再配visualMap调色。两者的差别在于heatmap更擅长表现密度,scatter则更容易控制点的大小、颜色和动效。
我用的是原生heatmap写法:
series: [ { type: 'heatmap', coordinateSystem: 'geo', pointSize: 12, blurSize: 20, data: heatData.value.map((item) => [item.lng, item.lat, item.value]) } ]数据格式必须是[经度, 纬度, 数值]。这个顺序我特别想强调,因为当时联调时后端给的字段名是lng和lat,但接口文档里写成了先纬度后经度,前端没注意就去 map 了,结果全国的点位全部跑到了海上,排查了半天才发现是坐标顺序反了。建议你在接入热力图前,先拿北京、上海、广州三个城市坐标试一下,确认位置正确再上全量数据。
3.4 与若依后端接口对接的正确姿势
若依后端接口使用统一的AjaxResult返回体,我定义的接口非常简单:
@GetMapping("/screen/heatData") public AjaxResult heatData() { return AjaxResult.success(screenService.getHeatList()); }前端继续走若依统一封装的request:
import request from '@/utils/request' export function fetchHeatData() { return request({ url: '/screen/heatData', method: 'get' }) }若依前端对 code 为 200 的响应会自动拆包,拿到data就直接是数组。如果你发现页面拿到undefined,大概率是接口被权限拦截,或者登录态失效,返回的数据结构不对。另外顺便提醒一下,若依代码生成时如果涉及雪花 ID 这种超长整型,后端返回给前端 JS 会有丢精度风险,最好在 DTO 里转成字符串输出。
4. 3D 地图扩展:用 ECharts-GL 把大屏做出立体层次
4.1 为什么引入 echarts-gl
大屏只有平面地图,在视觉上总觉得差一点。引入 echarts-gl 之后,可以在同一个 ECharts 实例里使用geo3D、map3D、scatter3D、bar3D等系列,不需要额外学一套 WebGL 绘制方案。
接入成本很低,安装依赖后直接引用即可:
import 'echarts-gl'项目实践里我比较推荐“先 2D 后 3D”的策略。第一版先保证业务数据链路正确,再在图表配置里新增一个geo3D基础配置,把普通map系列的data原封不动地交给scatter3D或bar3D,基本能做到平滑升级。
4.2 geo3D + map3D + scatter3D 的配合写法
我以全国数据大屏为例,先配置geo3D作为地理背景,再用scatter3D叠加业务点:
geo3D: { map: 'china', roam: true, itemStyle: { color: '#1a3d74', opacity: 0.8, borderColor: '#5bc1ff' }, label: { show: false }, emphasis: { label: { show: false } }, light: { main: { intensity: 1.2 }, ambient: { intensity: 0.3 } }, viewControl: { distance: 100, alpha: 40, beta: 10, autoRotate: true, autoRotateSpeed: 8 } }, series: [ { type: 'scatter3D', coordinateSystem: 'geo3D', data: heatData.value.map((item) => [item.lng, item.lat, item.value]), symbolSize: 8, itemStyle: { color: '#ffd04b' } } ]viewControl是控制 3D 视角的关键参数:distance是相机距离,alpha和beta控制俯仰角与水平角,autoRotate可以让地图缓慢自转,非常适合大屏长时间展示。唯一要注意的是自转速度不能太快,否则旁边的人看久了容易头晕。
4.3 规模大了以后,性能问题比效果更先出现
ECharts-GL 依赖 WebGL,这在大屏场景是隐藏风险。多数办公电脑跑 Chrome 没问题,但一旦遇到虚拟桌面、远程桌面环境,WebGL 很可能是被禁用的,3D 地图直接渲染不出来。
我当时加了 1 万个点位,开发机上很流畅,换到测试机就开始掉帧。最后做了四层优化:
- 后端先过滤低价值点位,前端只渲染 top 500 的数据;
- 交互时暂停
autoRotate,避免持续旋转和手动操作互相打架; - 3D 场景里避免同时叠加大量热力图,点位用
scatter3D,柱状效果用bar3D; - 增加 WebGL 可用性检测,不支持时自动回退到 2D 地图。
这套兜底逻辑上线之后省了很多事,至少没有出现客户打开大屏黑屏的尴尬。
5. 检索模块与大屏联动:完整交互链路与避坑实录
5.1 从搜索框到坐标定位的完整实现
大屏上的检索通常分两类:一类是通用地名搜索,比如搜某个街道、某个商圈;另一类是业务数据检索,比如门店名、设备编号。我的做法是二合一:搜索框输入关键词后,优先走若依后台接口查业务数据,每条结果带名称和经纬度;如果没有命中业务数据,再调用地图服务的搜索接口做兜底。
点击某条检索结果时,前端拿到经纬度,通过更新geo的center和zoom实现地图视角定位:
function handleSearchResult(row) { chart.setOption({ geo: { center: [row.lng, row.lat], zoom: 8 } }) // 如果需要让区域图 tooltip 弹出,还可以 dispatchAction chart.dispatchAction({ type: 'showTip', seriesIndex: 0, name: row.regionName }) }需要注意,showTip的name必须和区域图数据里的名称完全一致,否则 tooltip 不会出现。对于点位类型的数据,直接改series.data并高亮某个 symbol,效果更直观。
5.2 大屏在不同分辨率下的适配方案
大屏通常跑在超宽屏或高清电视上,开发设计尺寸一般按 1920×1080 来定。若依后台页面是正常网页布局,但大屏页面不适合跟随窗口宽度随意换行,我习惯用整体缩放方案:
.screen-container { width: 1920px; height: 1080px; transform-origin: left top; }function resizeScreen() { const scaleX = window.innerWidth / 1920 const scaleY = window.innerHeight / 1080 container.style.transform = `scale(${Math.min(scaleX, scaleY)})` }同时窗口尺寸变化时,要执行图表实例的chart.resize()。如果打开大屏后图表挤在左上角,绝大多数原因是容器初始高度为 0,或者初始化时机太早。外层的容器高度务必设成100vh,ECharts 初始化放在容器拿到高度之后,必要时用nextTick加一个延时兜底。
5.3 集成过程中让我印象深刻的四个坑
第一个坑是路由跳转后地图尺寸不对。大屏组件如果从后台菜单进入,组件挂载时过渡动画可能还没结束,拿到的高度是 0,图表就画在了 0 高度画布里。后来我把路由设为hidden + noCache,还在mounted里加了延时初始化,问题才稳定解决。
第二个坑是若依返回的创建时间少了 8 小时。后端实体用LocalDateTime时,前端解析经常按 UTC 处理,在大屏的时间轴数据上特别容易暴露。处理办法是后端统一在字段上加@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss"),不要指望前端自己判断时区。
第三个坑是坐标顺序,前面已经提到,不重复展开。只强调一句:接入热力图前先用已知城市坐标自测一遍,比上线后发现全图点位漂移要省事得多。
第四个坑是长时间挂屏导致的内存泄漏。大屏通常 7×24 小时开着,我用定时器刷新数据,最初直接在回调里反复chart.setOption,跑了一天之后页面越来越卡。现在的做法是刷新前先chart.clear(),离开页面时chart.dispose(),同时把定时器和 resize 监听一起清掉。Vue2 里在beforeDestroy清理,Vue3 要换到onBeforeUnmount。
如果这块大屏本身没有复杂的轮播需求,数据刷新频率建议控制在 30 秒到 1 分钟一次,不用把动画做满帧,既省设备资源,也能让前端图表稳定运行更久。
本文还有配套的精品资源,点击获取