ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

GeoJSON边界数据获取与省市区县街道多级联动实战指南

GeoJSON边界数据获取与省市区县街道多级联动实战指南 搞地图可视化的同学八成都被边界数据折磨过。做“省市区县街道”这种多级联动最麻烦的不是写交互而是“上哪弄一份干净、最新、还带层级关系的 GeoJSON 边界”。网上找的数据要么精度差要么坐标系对不上要么只到省市级想拿到街道级还得手动去爬接口。2026 年了其实很多数据源已经变稳定了这套流程我自己跑了大半年整理成一篇从“获取边界数据”到“多级联动实现”再到“自动更新下载”的完整记录适合做 Web GIS、数据大屏、政务可视化或地图产品的朋友直接抄作业。这篇内容会围绕三件事展开第一GeoJSON 边界数据到底怎么拿省市区县街道各级分别有哪些可靠渠道第二拿到数据之后怎么组织、裁剪、按需加载把多级联动做顺第三我会把定时更新脚本和一份实时可下载的数据文件说明放在文末你可以直接用也可以在自己的服务器上定期跑一遍。1. 边界数据为什么难搞先把数据模型和坐标系理清楚1.1 GeoJSON 只是外壳真正的核心是行政区划代码说句大实话GeoJSON 这种格式本身不复杂。一个 FeatureCollection里面装一堆 Polygon 或 MultiPolygon每个 Feature 的 properties 放名字、区域代码前端用 Leaflet、Mapbox、ECharts 画出来就完事。真正让“省市区县街道多级联动”变复杂的是这些 Feature 之间的父子关系怎么表达。我最早拿到一份全国省界 GeoJSON 后直接在前端画出来了看起来很爽。可当我点击某个省想下钻到市的时候傻了——数据里根本没有“这个省包含哪几个市”的结构。所以后来我统一用行政区划代码作为主键。中国的行政区划代码是一串有规律的数字比如北京市东城区是 110101其中 11 是省级1101 是市级110101 是区县级后面还可以继续挂街道/乡镇代码。这个代码天然就是一棵树想找孩子的父亲只要根据代码前缀去匹配就行。明白了这个道理你再去组织数据就会很清晰。我习惯在 GeoJSON 的每个 Feature 里至少维护这几个字段adcode行政区划代码唯一标识联动查询的主键name行政区名称给人看的level省/市/区县/街道方便前端知道当前层级parent父级 adcode没有也行但有了可以快速反查childrenNum子级数量用于判断是否需要继续下钻用中文名做多级联动非常不可靠。全国有大量重名或相似地名而且行政区划调整后名称会变但代码体系相对稳定。我见过有人用 name 去匹配子级接口结果“鼓楼区”一下子查出好多个整个地图就乱了。所以记住一句话一切联动都用 adcodename 只负责展示。1.2 “最新”和“实时”到底是什么意思很多人找数据时喜欢搜“实时边界”但实际上边界数据不存在真正意义上的实时接口。行政区划调整是低频事件一般是撤销县设区、合并乡镇、街道拆分等每年会有批次调整通常以官方发布的行政区划代码变更为准。所以我们要做的不是“每秒钟都去抓一次”而是保证数据源足够新并且保留“更新时间”的痕迹。我自己的做法是在数据目录里放一个version.json记录updated_at和source。每次跑完更新脚本把日期写进去前端调试时一眼就能看出这份数据是哪天的快照。这里也提醒一句网上很多免费边界数据包文件名写着“最新”打开才发现是两三年前的。更坑的是有些平台抓取后做过简化处理边界线条很粗糙贴近一看完全对不上路网。因此如果你在乎边界精度最好选择稳定的数据源并且在项目里明确记录来源后续出问题也好排查。1.3 WGS84、GCJ-02、BD-09坐标系不对地图直接错位多级联动最容易踩的隐形坑是坐标系。不同数据源的经纬度参考系不一样混用的时候地图会整体偏移几十到几百米。简单解释一下WGS84 是 GPS 用的国际标准很多开源地理数据和天地图数据都用它GCJ-02 是国内大部分地图厂商做加密后的坐标系高德、腾讯都基于它BD-09 是百度在 GCJ-02 上再次偏移的一套坐标。这三个坐标系的经纬度数值在平面地图上肉眼可能看不出问题但一旦叠加到底图或路网上道路和行政边界就会对不齐。我现在的原则是在进入项目前先确认数据源的坐标系然后统一转换。比如拿高德接口拿到的街道边界我会先用 coordtransform 之类的库批量转成 WGS84 再入库如果项目用的底图是高德那就反过来统一成 GCJ-02。别嫌麻烦这一步能省掉后面所有“地图飘了”的排查时间。2. 2026年依然能用的几种边界数据获取方法2.1 阿里云 DataV.GeoAtlas省市区县数据最省心的来源做前端可视化的小伙伴应该都见过 DataV.GeoAtlas它提供了一套非常方便的行政区划 GeoJSON 下载接口URL 规律很清晰https://geo.datav.aliyun.com/areas_v3/bound/{adcode}.json https://geo.datav.aliyun.com/areas_v3/bound/{adcode}_full.json其中不带_full的是本级行政区划的单个边界带_full的是该行政区下一级子区域的合集。举个例子100000.json是中国的整体轮廓100000_full.json是 34 个省级行政区的完整集合440000_full.json是广东省所有地市级的集合440100_full.json则是广州市所有区县的集合。这套接口对省、市、区县三级覆盖非常完整特别适合做“省→市→区县”的下钻联动。拿回来的 GeoJSON 里 properties 同样包含 adcode、name、childrenNum 等字段前端代码几乎不需要额外关联处理。我实测下来数据质量比较稳定边界线条相对平滑而且它是按需请求单级数据文件体积不大加载速度在可接受范围内。不过它的短板也很明显到区县一级基本就停了街道/乡镇级别的边界DataV.GeoAtlas 并没有覆盖。所以如果你的业务需要下钻到街道就得再找别的数据源补一段。2.2 高德行政区查询接口可以补上街道级边界高德开放平台的“行政区查询”接口是做街道数据的重要补充。它可以用一个关键词把某区域的子级行政区划全部列出来甚至返回边界经纬度串。接口大概长这样https://restapi.amap.com/v3/config/district?key你的Keykeywords广州市subdistrict1extensionsall几个关键参数要认真设置keywords要查询的父级区域名配合 adcode 或 name 都行subdistrict需要返回下级行政区的层级数量1 表示直接子级2 表示一直到区县3 可以到街道/乡镇extensionsall返回边界坐标如果不带all默认只返回基础信息没有轮廓用这个接口拉街道级数据时我踩过几个坑。第一高德返回的坐标是 GCJ-02不是 WGS84如果直接塞给 Leaflet 默认瓦片边界会偏移第二polyline是一个规划好的经纬度字符串你需要解析成数组再转成 GeoJSON 的 Polygon不能直接当 GeoJSON 用第三街道/乡镇边界精度不稳定有的乡镇返回的线条很粗略做数据大屏可以做精确分析要谨慎。另外高德接口限制了每天调用量按街道级递归抓取时全国几千个乡镇/街道需要分批、加延时、加失败重试。我建议尽早在代码里做好限流避免触发日配额。2.3 天地图和官方代码表需要严谨场景时的备案如果你的项目对数据来源的权威性要求很高比如政务平台、城市规划类应用那可以考虑从官方公开服务获取。天地图平台提供统一的空间数据服务也包含部分行政区划边界资源而行政区划代码表可以找到官方发布的“中华人民共和国行政区划代码”标准文件里面包含了最新的省、市、区县代码。用官方代码表的好处是“来源清晰、可追溯”但实操上比较折腾代码表只有文字没有边界几何天地图虽然有服务却需要申请开发者 Key而且接口风格相对传统返回格式不像 DataV 那么“开箱即用”。所以我一般把它当作校验辅助先用 DataV 和高德抓取边界再用官方代码表核对 adcode 集合是否完整、有没有新设或撤销的区划。2.4 开源 GitHub 仓库与第三方 GeoJSON 数据站GitHub 上有不少行政区划数据仓库比如专门整理省市区街道层级 JSON 的项目从省级到村级的代码和名称都有也可以找到别人打包好的边界 GeoJSON。这类仓库优势是唾手可得缺点是你无法确定它多久更新一次数据源是否有版权风险以及简化精度是否满足要求。我的经验是GitHub 数据可以用来“临时演示”和“快速原型”但不建议直接丢到生产环境。如果你用了某个仓库的数据至少在 README 或代码里注明来源、抓取时间、坐标系方便后续维护者替换。另外有条件的可以基于这些仓库 fork 一份然后配合自己的更新脚本定期刷新。2.5 常见数据源对比速查表数据源覆盖层级更新频率参考坐标系获取方式适合场景阿里云 DataV.GeoAtlas省、市、区县不定期随数据源更新通常 WGS84/GCJ-02 需自查静态 URL 下载前端可视化、下钻联动高德开放平台省、市、区县、街道乡镇较及时GCJ-02API 查询并解析街道级边界补全天地图服务多级行政区划按国家发布节奏CGCS2000/WGS84开发者 Key 服务调用政务平台、严谨项目官方行政区划代码表省、市、区县以发布公告为准无几何数据文件下载校验 adcode、更新列表GitHub 数据仓库不定通常到乡镇不定各异clone / 下载原型演示、离线数据包3. 省市区县街道多级联动的完整实现思路3.1 数据目录与文件命名规范拿到一堆 GeoJSON 后别直接扔给前端先按规则整理。我常用的目录结构是data/ ├── version.json ├── province.json # 省级面集合 ├── city/ │ ├── 440000.json # 广东省市级面集合 │ └── ... ├── district/ │ ├── 440100.json # 广州市区县级面集合 │ └── ... └── street/ ├── 440106.json # 广州市天河区街道级面集合 └── ...这样做的好处是前端拿到一个adcode可以直接拼出对应子级数据文件的 URL。比如点击了广东省的440000就去请求city/440000.json里面就是广东省所有地级市的边界点击广州市的440100就去请求district/440100.json得到所有区县。街道级同理。文件命名规则要严格遵守“用父级 adcode 命名子级集合”不要用中文拼音或自定义编号。不要问我为什么凡是自定义编号的后续基本上都要返工。3.2 在 Leaflet 里实现点击下钻用 Leaflet 做行政边界下钻是我觉得最直观的方式。核心思路是初始加载province.json给每个 Feature 绑定点击事件点击后读取feature.properties.adcode然后移除当前图层加载对应的子级文件。下面是一段可直接参考的示例代码let map L.map(map).setView([35, 105], 4); L.tileLayer(https://tile.openstreetmap.org/{z}/{x}/{y}.png, { maxZoom: 18 }).addTo(map); let currentLayer null; function loadBoundary(url, adcode, name) { fetch(url) .then(res res.json()) .then(geoJson { if (currentLayer) { map.removeLayer(currentLayer); } currentLayer L.geoJSON(geoJson, { style: { color: #2a7fff, weight: 1, fillOpacity: 0.2 }, onEachFeature: (feature, layer) { let props feature.properties || {}; layer.on(click, () { let childCode props.adcode; let childUrl getChildUrl(props.level, childCode); if (childUrl) { map.flyToBounds(layer.getBounds(), { maxZoom: 10 }); loadBoundary(childUrl, childCode, props.name); } }); layer.bindTooltip(props.name); } }).addTo(map); map.flyToBounds(currentLayer.getBounds(), { maxZoom: 10 }); }); } function getChildUrl(level, adcode) { if (level 省) return data/city/${adcode}.json; if (level 市) return data/district/${adcode}.json; if (level 区县) return data/street/${adcode}.json; return null; } loadBoundary(data/province.json, 100000, 中国);这段代码已经把下钻的骨架写出来了。实际项目里还要处理一个细节并不是所有行政区划都是严格的“省→市→区县”三级。比如北京、上海、天津、重庆这四个直辖市它们下面直接就是区县根本没有中间的“地级市”层。这种情况下如果你在省级层面请求city/110000.json大概率会失败或得到空数据。我的处理方式是在生成数据时加一个level字段前端判断如果当前省是直辖市就从省级直接跳到district/。3.3 在 ECharts 里实现多级联动如果你主要是做数据大屏那会更常用 ECharts。ECharts 的map系列支持通过registerMap动态注册 GeoJSON然后结合dispatchAction实现下钻。核心流程是初始registerMap(china, provinceGeoJson)series的map设为china监听click事件得到参数里的adcode或name根据adcode请求下一级 GeoJSON再registerMap(child, childGeoJson)更新 series 的 map 属性或直接 setOption 换数据很多 ECharts 示例只做到省市级区县以下会比较卡。这里给你一个更稳妥的小技巧与其在 ECharts 内部做复杂联动不如引入一个“外部状态机”也就是用 Leaflet 做真正的联动切换用 ECharts 只做当前层级的统计展示。两者通过adcode同步这样代码清晰性能也可控。3.4 边界简化与数据瘦身拿到了高精度边界不等于可以直接塞到页面上。全国省级边界文件动辄几 MB全部加载对网络很不友好。我一般要经过三步瘦身GeoJSON → TopoJSON、拓扑简化、Gzip 传输。如果你不熟悉 TopoJSON可以理解成它把相邻边界做成共享边能大幅减少重复坐标文件体积经常能减少 50% 以上。ECharts、Mapbox 都能直接消费 TopoJSONLeaflet 需要先转回 GeoJSON。简化边界我常用mapshaper安装后一条命令就能完成npx mapshaper input.json -simplify 15% -o output.json这个15%表示保留 15% 的顶点看起来牺牲很大但只要设置合理肉眼几乎看不出区别。对于街道级数据因为本身很精细我建议简化比例不要低于 20%否则边界会明显“多边形化”。简化后顺手做一个gzip压缩。如果你是放在 Nginx 或 CDN 上直接开启gzip_staticGeoJSON 这类文本文件的压缩率非常高很多时候能减到原来的 20% 大小。不要在客户端单独解压交给浏览器和服务器自动协商就好。3.5 加上缓存和并发控制多级联动如果每次都通过网络请求数据文件区域越小、访问越快但用户重复点击同一个市也会产生不必要的流量。我会在前端用 localStorage 做一层缓存const cacheKey geo:${adcode}; let geoData localStorage.getItem(cacheKey); if (geoData) { renderBoundary(JSON.parse(geoData)); } else { fetch(data/${adcode}.json) .then(res res.json()) .then(data { localStorage.setItem(cacheKey, JSON.stringify(data)); renderBoundary(data); }); }注意localStorage 的容量一般在 5MB 左右所以只缓存区县和街道级这种小体积文件省级的 100000_full.json 体积大不建议缓存。并发方面如果一次要加载多个相邻区域的边界比如从省级集合里一次性加载所有市的轮廓可以用Promise.all并发请求但要控制并发数在 5~6 个左右性能测试后具体调整。4. 本地部署 geojson.io 搭建自己的边界工作台4.1 为什么值得本地部署在线版 geojson.io 一直是处理 GeoJSON 的利器图层拖拽、手绘编辑、导出都非常顺手。但对经常处理行政区划数据的人和内网开发环境来说在线版有两个痛点一是公司的边界数据不方便传到第三方服务二是网络不稳定时在线编辑器卡顿明显体验很受影响。本地部署一个 geojson.io 的副本好处是数据完全留在自己手里编辑大文件时响应也更快。你不需要重新造轮子只需把开源项目拉起来用浏览器打开就能干活。4.2 通过 Docker 快速部署geojson.io 本身是一个前端应用部署并不复杂。我习惯用 Docker 封装避免污染本机 Node 环境。可以参考下面这个 DockerfileFROM node:16-alpine WORKDIR /app RUN apk add --no-cache git RUN git clone https://github.com/mapbox/geojson.io.git . RUN npm install --legacy-peer-deps RUN npm run build EXPOSE 8080 CMD [npm, start]构建并启动docker build -t geojson-io . docker run -d -p 8080:8080 --name geojsonio geojson-io有一点要提前说明geojson.io 这个项目更新频率不算高对较新的 Node 版本可能有兼容问题。我实际测试发现 Node 16 环境下比较稳如果你本机已经装了 Node 20建议用 Docker 而不是直接源码跑。4.3 源码方式部署与接入自定义底图如果你不想用 Docker也可以直接把仓库 clone 到本地git clone https://github.com/mapbox/geojson.io.git cd geojson.io npm install npm start本地默认跑在 8080 端口浏览器打开http://localhost:8080。一个常见问题是它默认依赖 Mapbox 的底图 token没有 token 时底图加载不出来。但我们要编辑的是本地 GeoJSON底图缺失影响并不致命。如果你希望底图也能正常显示最简单的办法是修改代码里的瓦片配置把默认底图换成开放的 OSM 瓦片具体思路是找到初始化地图的tileLayer部分替换成L.tileLayer(https://tile.openstreetmap.org/{z}/{x}/{y}.png, { attribution: copy; OpenStreetMap contributors })改完后重新构建打开编辑器就能看到普通地图了。4.4 用 geojson.io 做边界数据的质检工作流我本地部署 geojson.io 之后主要不拿它来画图而是当一个“质检器”。每次从数据源下载完新的边界 GeoJSON我都会用浏览器打开它做三件事第一肉眼查看边界是否闭合、是否有飞地或异常凹角第二拖入多个层级文件检查父子边界是否重叠、相邻区域是否压盖第三把坐标点和坐标串导出成小文件交给脚本做程序化校验这种“人眼脚本”的双重检查很有效。边界数据最怕的不是格式错误而是“看起来对”的小瑕疵。等你上线之后才发现边界重叠用户点选某个区县时连别的区县也高亮那个返工成本就高了。5. 实时更新脚本与文末数据下载实战5.1 用 Python 批量抓取省市区县 GeoJSON前面的方式都是手动操作可持续的做法是写脚本定时抓取。我写了一个 Python 脚本逻辑并不复杂先从100000_full.json拿到省级 adcode 列表然后对每个省级 adcode 请求_full.json获取市级再对每个市级 adcode 请求_full.json获取区县级最后按目录规则落盘。下面是一个精简版示例import json import os import requests import time BASE https://geo.datav.aliyun.com/areas_v3/bound/{adcode}_full.json DATA_DIR data def download(url, path): os.makedirs(os.path.dirname(path), exist_okTrue) resp requests.get(url, timeout20) resp.raise_for_status() with open(path, w, encodingutf-8) as f: json.dump(resp.json(), f, ensure_asciiFalse) time.sleep(0.5) def main(): # 1. 省级 download(BASE.format(adcode100000), f{DATA_DIR}/province.json) provinces json.load(open(f{DATA_DIR}/province.json, encodingutf-8)) province_list [(f[properties][adcode], f[properties][name]) for f in provinces[features]] for adcode, name in province_list: # 2. 市级 url BASE.format(adcodeadcode) download(url, f{DATA_DIR}/city/{adcode}.json) city_data json.load(open(f{DATA_DIR}/city/{adcode}.json, encodingutf-8)) city_list [(f[properties][adcode], f[properties][name]) for f in city_data[features]] for city_adcode, city_name in city_list: # 3. 区县级 download(url.replace(str(adcode), str(city_adcode)), f{DATA_DIR}/district/{city_adcode}.json) if __name__ __main__: main()这个脚本有几个可供优化的点一是加错误重试因为网络请求可能超时二是做“增量更新”当某个 adcode 的下载文件已经存在且日期没变时可以直接跳过三是记录整个抓取过程的时间戳方便生成version.json。5.2 街道级数据抓取高德接口限流与解析街道级数据我上面提过要高德接口来做。这里的重点不是调用本身而是怎么在可控配额之内递归抓完全国所有街道。我的做法是维护一个任务队列先读取已经下载好的区县 adcode 列表然后逐个请求高德接口每次subdistrict1只要解析出districts里包含的街道/乡镇列表就保存成 GeoJSON。请求之间至少sleep(0.3)并且加了简单的指数退避重试。再补充一个解析细节高德返回的polyline字段是一个用分号切分坐标对的字符串长这样116.397428,39.90923;116.3981,39.9095;...把它切分后转成数组就能生成 Polygon 或 MultiPolygon。注意整个轮廓可能由多个闭合线段组成需要判断第一个点和最后一个点是否相等不等时要手动闭合。5.3 用 GitHub Actions 定时跑更新任务脚本写好后下一步是让它自动跑。最省钱最省事的方案是放在 GitHub Actions 里用 cron 定时触发。下面是一个典型的工作流文件name: update-geo-boundary on: schedule: - cron: 0 2 * * * workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkoutv4 - name: setup python uses: actions/setup-pythonv5 with: python-version: 3.11 - name: install deps run: pip install requests - name: run update script run: python scripts/update_geo_data.py - name: commit and push run: | git config user.name github-actions git config user.email actionsgithub.com git add data version.json git commit -m chore: update geo boundary data $(date %F) || echo no changes git push这样每天凌晨两点脚本会自动抓取最新数据有变化就提交没变化就跳过。前端站点如果部署在 GitHub Pages 或静态服务器上每次更新后自动就可以通过data/latest/province.json这类路径取到最新数据。5.4 文末附实时 GeoJSON 数据下载说明按前面这套流程最终你会得到一个包含以下文件的实时数据目录data/latest/province.json全国省级边界data/latest/city/{adcode}.json各省市级边界data/latest/district/{adcode}.json各区县级边界data/latest/street/{adcode}.json各街道/乡镇边界data/latest/version.json数据版本与更新时间如果你不想自己搭一遍 GitHub Actions也可以直接使用我在公开静态站上发布的实时数据包路径格式和上面保持一致。拿province.json举例只要把地址换成你部署的域名就能直接拿到最新省级 GeoJSON。有一点要特别注意即使是从公开服务抓取的数据也建议在项目里标明数据来源和更新时间。不要直接把这些数据用于高精度测绘或任何需要行政确权的业务场景它更适合做可视化展示、产品原型和宏观分析。如果业务场景对精确边界有硬性要求应当购买或申请官方权威数据。6. 常见问题与排查技巧实录6.1 边界整体错位怎么办如果你发现边界整体偏移了几十米或几百米先不要怀疑数据坏了十有八九是坐标系没统一。排查步骤很简单先在代码里打点拉一条线输出边缘点的经纬度再和已知坐标点对比如果和地图底图有系统性偏移就把数据统一转换坐标系。别去一个一个手改经纬度写个转换函数批量跑一遍即可。6.2 点击省后没有任何下级数据这种问题的根源通常有两个一是该省级 adcode 没有子级文件比如直辖市二是你的数据抓取脚本漏掉了某些特殊行政区划比如省直辖县级市、直筒子市。直筒子市的意思是没有区县这个层级、直接下辖乡镇街道常见的有广东东莞、中山等。遇到这种情况前端要做两件事第一判断当前 Feature 的childrenNum是否为 0如果是就不要再请求子级而是跳转到街道级别第二在数据更新脚本里专门处理这类特殊区域的街道数据直接为它们生成street/{adcode}.json跳过district/目录。6.3 街道级数据质量差怎么补救街道级边界目前的数据源普遍不如省市区县稳定。有的乡镇边界在高德接口里只有一条很粗糙的折线有的甚至只能拿到中心点。我的建议是如果你的应用场景只是展示“我大概在这个街道”而不是做真正的边界裁决那么在数据上叠加一个“模糊范围”效果或者用街道中心点加半径圈即可。不要为了所谓精度强行扭曲不准确的数据那样反而显得整体地图质量很低。6.4 页面加载缓慢怎么优化多级联动越往后层级越小理论上数据越轻真正容易卡的是省级全图和市级包含子级的全量文件。除了前面提到的简化和 TopoJSON还可以从加载策略上优化一开始只加载省级边界点击某个省后再加载市级边界切到市级后再加载区县街道级干脆不预加载。另外配合 Vue 或 React 这类框架时注意不要把所有 GeoJSON 都放在内存里。每次切换层级时用 map 实例移除当前图层并释放引用避免内存只增不减。数据量实在大的时候可以用 Leaflet 的L.geoJSON(geojson, {style})配合filter过滤不需要的 Feature而不是一次性把整个集合塞进去。6.5 版权和商用提醒写到最后想认真提醒一句地图边界数据不是“谁传的都能随意商用”。开源数据站和官方公开接口的使用规则各不相同有的要求署名有的一定量以下可以免费商用有的仅供演示。上线前务必确认你使用的数据源是否允许商业用途并且把授权信息、来源链接一并归档。数据合规这件事等收到通知再去补成本就高很多了。我个人在实际操作中的习惯是所有数据都保留抓取脚本和来源记录每条数据都打上更新日期每次行政区划调整后重新跑一遍更新脚本再对比version.json中的差异。这套流程看起来简单但它能让我不用再为“边界到底是不是最新的”而焦虑。如果你也准备做省市区县街道多级联动建议今天就先把数据源和目录规则定下来再写前端交互后面会顺很多。
返回列表