桌面大爷学Web(2)-AI SOLO模式实战:只动嘴不动手从零构建Vue地图页面

桌面大爷学Web(2)-AI SOLO模式实战:只动嘴不动手从零构建Vue地图页面

文章目录

一、前言

上一篇文章里,学会了搭建第一个Vue应用。本文,通过Trae的SOLO模式,做一个具备简单功能的Web页面。近年来,在软件开发领域,AI辅助编程已经成为一种趋势。最近,老丁体验了Trae IDE的AI SOLO模式,印象挺深!本文记录用AI SOLO开发一个基于Vue 3和OpenLayers的地图应用的全过程,展示AI在实际项目开发中的表现。

SOLO

二、 项目背景

我们需要开发一个地图应用,主要功能包括:

  • 显示本地摩卡托投影瓦片地图
  • 支持双图层显示(底图+DEM等高线)
  • 提供地图交互功能(缩放、漫游、重置视图等)
  • 实现量测功能
  • 集成SQLite数据库查询功能

技术栈选择:

  • 前端:Vue 3 + Vite + OpenLayers 9
  • 后端:Flask + SQLite

其产品界面如下:

HF

SOLO 是采用对话的方式,人类提需求,AI自主编程。
为了配置Trae,我们提前在PATH里安装好了Python、NodeJS,在Trae里配置了Node、Python等扩展插件,并用手工传统编程模式跑通了Vue的Hello-World例子。同时,在PATH里安装了Git,保证每个子任务最后都可以以git提交的模式直接签入。

三、交互开发流程

第一次任务:项目初始化

任务描述:创建Vue 3项目,集成OpenLayers,实现基础地图显示

AI SOLO模式的表现让我眼前一亮。当我提出需求后,AI立即:

  1. 创建了完整的项目结构
  2. 配置了Vite构建工具
  3. 安装了必要的依赖(Vue 3、OpenLayers 9)
  4. 实现了基础的地图显示功能

关键代码片段:

// MapView.vue - 地图初始化import Map from'ol/Map'import View from'ol/View'import TileLayer from'ol/layer/Tile'importXYZfrom'ol/source/XYZ' map =newMap({target: mapRef.value,layers:[newTileLayer({source:newXYZ({url:'http://127.0.0.1:8087/osm_tiles/{z}/{x}/{y}.png',maxZoom:20,minZoom:0})})],view:newView({center:[0,0],zoom:2,maxZoom:20,minZoom:0})})

评价:AI准确理解了需求,代码结构清晰,符合Vue 3 Composition API的最佳实践。

第二次任务:地图交互功能

任务描述:添加缩放控制、漫游功能、重置视图、经纬度网格显示、鼠标位置实时显示

AI在理解需求后,迅速实现了这些功能:

  1. 缩放控制:实现了按钮缩放和鼠标滚轮缩放
  2. 漫游功能:利用OpenLayers自带的拖拽功能
  3. 重置视图:一键恢复初始视图
  4. 经纬度网格:使用Graticule图层实现
  5. 鼠标位置显示:实时显示经纬度坐标

关键实现:

// 缩放控制constzoomIn=()=>{const view = map.getView()const zoom = view.getZoom() view.setZoom(zoom +1)}// 经纬度网格 graticuleLayer =newGraticule({strokeStyle:newStroke({color:'rgba(255, 0, 0, 0.5)',width:1}),showLabels:true,wrapX:false})// 鼠标位置 map.on('pointermove',(evt)=>{const coords = evt.coordinate mouseCoordinates.value ={lon: coords[0],lat: coords[1]}})

评价:AI对OpenLayers的API非常熟悉,代码实现简洁高效,UI布局合理。

第三次任务:模式选择与量测功能

任务描述:实现漫游模式和量测模式的切换,以及量测距离的功能

这是最复杂的任务之一,AI的表现依然出色:

  1. 模式切换:实现了漫游模式和量测模式的切换UI和逻辑
  2. 量测功能:使用OpenLayers的getLength函数计算距离
  3. 交互设计:支持多点量测,双击结束量测,实时显示距离

核心实现:

// 量测功能conststartMeasure=()=>{ isMeasuring.value =true measureResult.value =nullconsthandleClick=(evt)=>{if(!isDrawing){ isDrawing =true coordinates =[evt.coordinate]const feature =newFeature({geometry:newLineString(coordinates)}) sketch = feature measureSource.addFeature(feature)}else{ coordinates.push(evt.coordinate)if(sketch){ sketch.getGeometry().setCoordinates(coordinates)}}}consthandleDoubleClick=(evt)=>{if(isDrawing){ isDrawing =falseconst geometry = sketch.getGeometry()const output =formatLength(geometry) measureResult.value = output stopMeasure()}}}constformatLength=(line)=>{const length =getLength(line,{projection:'EPSG:3857'})const kilometers = length /1000return kilometers.toFixed(3)}

评价:AI准确理解了量测的交互逻辑,代码实现完整,考虑了各种边界情况(如拖拽时忽略事件)。

SOLO1

第四次任务:数据库查询功能

任务描述:集成SQLite数据库,实现按日期查询测试记录,并在地图上显示结果

这个任务涉及前后端的协作,AI的表现依然令人满意:

后端实现(Flask):

@app.route('/api/query_by_date', methods=['GET'])defquery_by_date(): date_str = request.args.get('date')ifnot date_str:return jsonify({'error':'请提供日期参数'}),400try: datetime.strptime(date_str,'%Y-%m-%d')except ValueError:return jsonify({'error':'日期格式不正确,请使用 YYYY-MM-DD 格式'}),400 conn = get_db_connection() cursor = conn.cursor() cursor.execute(''' SELECT test_id, test_time, test_name, test_lat, test_lon FROM test_table WHERE DATE(test_time) = ? ORDER BY test_time ''',(date_str,)) rows = cursor.fetchall() conn.close() results =[]for row in rows: results.append({'test_id': row['test_id'],'test_time': row['test_time'],'test_name': row['test_name'],'test_lat': row['test_lat'],'test_lon': row['test_lon']})return jsonify({'date': date_str,'count':len(results),'data': results })

前端实现

constqueryData=async()=>{if(!selectedDate.value){alert('请选择日期')return}try{clearTestLayer()const response =awaitfetch(`http://127.0.0.1:5000/api/query_by_date?date=${selectedDate.value}`)const data =await response.json()if(data.error){alert(data.error)return} queryResult.value = data if(!testLayer){createTestLayer()}const source = testLayer.getSource() data.data.forEach(item=>{const coords =transform([item.test_lon, item.test_lat],'EPSG:4326','EPSG:3857')const feature =newFeature({geometry:newPoint(coords),test_id: item.test_id,test_time: item.test_time,test_name: item.test_name }) source.addFeature(feature)})if(data.data.length >0){const extent = source.getExtent()const view = map.getView() view.fit(extent,{padding:[50,50,50,50],maxZoom:15})}}catch(error){ console.error('查询失败:', error)alert('查询失败,请检查服务器是否运行')}}

评价:AI正确处理了坐标转换(WGS84到Web Mercator),实现了自动缩放到数据区域的功能,错误处理也很完善。

第五次任务:地名搜索功能

任务描述:集成OSM地名查询服务,实现按名称搜索地标位置,并在地图上显示结果

这个任务涉及第三方API集成和CORS跨域问题处理,AI的解决方案非常专业:

前端实现

constsearchPlaceByName=async()=>{if(!searchPlace.value){alert('请输入地名')return}try{clearSearchPlaceLayer()const encodedName =encodeURIComponent(searchPlace.value)const response =awaitfetch(`/api/query_osm/?indented=1&function=object_by_name&name=${encodedName}&submit=submit`)const data =await response.json()if(data.result !=='succeeded'){alert('查询失败')return} searchResult.value = data if(!searchPlaceLayer){createSearchPlaceLayer()}const source = searchPlaceLayer.getSource()for(let i =0; i < data.items; i++){const resultKey =`result${i}`const result = data[resultKey]if(result && result.center_pos){const coords =parseCenterPos(result.center_pos)if(coords){const transformedCoords =transform([coords.lon, coords.lat],'EPSG:4326','EPSG:3857')const feature =newFeature({geometry:newPoint(transformedCoords),name: result.name,osm_id: result.osm_id }) source.addFeature(feature)}}}if(data.items >0){const extent = source.getExtent()const view = map.getView() view.fit(extent,{padding:[50,50,50,50],maxZoom:15})}}catch(error){ console.error('查询失败:', error)alert('查询失败,请检查服务器是否运行')}}constcreateSearchPlaceLayer=()=>{const vectorSource =newVectorSource() searchPlaceLayer =newVectorLayer({source: vectorSource,style:newStyle({image:newCircle({radius:7,fill:newFill({color:'rgba(0, 0, 139, 0.5)'})})})}) map.addLayer(searchPlaceLayer)}constparseCenterPos=(centerPos)=>{const match = centerPos.match(/POINT\(([\d.]+) ([\d.]+)\)/)if(match){return{lon:parseFloat(match[1]),lat:parseFloat(match[2])}}returnnull}

Vite代理配置(解决CORS问题):

// vite.config.jsexportdefaultdefineConfig({plugins:[vue()],server:{port:3000,https:false,strictPort:true,host:true,proxy:{'/api/query_osm':{target:'http://127.0.0.1:8087/query_osm',changeOrigin:true,rewrite:(path)=> path }}}})

评价:AI准确理解了OSM查询服务的API格式,正确解析了POINT格式的坐标数据,并通过Vite代理优雅地解决了CORS跨域问题。深蓝色半透明圆形标记的样式实现完全符合需求。

SOLO

最终任务 应用代码重构和添加注释

随着项目功能的不断完善,MapView.vue 文件逐渐膨胀到 754 行,包含了地图初始化、数据查询、地名搜索、量测功能、地图控制等多个功能模块。这种单一文件包含过多职责的代码组织方式存在以下问题:

  1. 可维护性差 - 修改一个功能需要在一个大文件中查找相关代码
  2. 可复用性低 - 业务逻辑与 UI 耦合,难以在其他项目中复用
  3. 可测试性差 - 复杂的组件难以编写单元测试
  4. 团队协作困难 - 多人同时修改一个大文件容易产生冲突

重构目标

  • 将 MapView.vue 从 754 行减少到合理规模
  • 按功能职责拆分为多个子组件
  • 提取可复用的业务逻辑到 composables
  • 提取纯函数到 utils
  • 保持所有功能不变

重构方案

目录结构:

src/ ├── components/ # UI 组件 │ ├── MapView.vue # 主容器组件 (117行) │ ├── Sidebar.vue # 侧边栏 - 查询和搜索 │ ├── ModeSelector.vue # 模式选择器 │ ├── MapControls.vue # 地图控制按钮 │ ├── MeasureControls.vue # 量测控制 │ ├── MeasureResult.vue # 量测结果显示 │ └── MousePosition.vue # 鼠标位置显示 ├── composables/ # 可复用逻辑 │ ├── useMap.js # 地图初始化和基础操作 │ ├── useDataQuery.js # 数据查询功能 │ ├── usePlaceSearch.js # 地名搜索功能 │ └── useMeasure.js # 量测功能 ├── utils/ # 工具函数 │ ├── mapUtils.js # 地图工具函数 │ └── api.js # API 调用函数 ├── App.vue └── main.js 

模块划分:

  • Utils 层 - 纯函数和 API 调用
  • Composables 层 - 可复用业务逻辑
  • Components 层 - UI 组件
  • MapView.vue - 主容器组件

重构成果

文件重构前重构后变化
MapView.vue754 行117 行-84.5%
总计754 行117 行 + 12 个新文件模块化
  • Utils: 2 个文件
  • Composables: 4 个文件
  • Components: 6 个文件

重构优势

  1. 单一职责原则
    每个组件和函数只负责一个功能,符合 SOLID 原则。
  2. 高复用性
  • Composables 可以在其他项目中复用
  • Utils 中的纯函数可以在任何地方使用
  1. 易维护性
    代码结构清晰,修改某个功能只需关注对应文件,不需要在一个大文件中查找。
  2. 易测试性
    独立的函数和组件更容易编写单元测试。

构建验证

重构完成后,项目构建成功,无任何错误:

npm run build vite v5.4.21 building for production... ✓ 229 modules transformed. dist/index.html 0.41 kB │ gzip: 0.30 kB dist/assets/index-CUmN_D1s.css 8.62 kB │ gzip: 2.07 kB dist/assets/index-B2uDXUdW.js 388.77 kB │ gzip: 120.35 kB ✓ built in1.74s 

四 AI SOLO模式的优势总结

通过这次合作开发,感觉对通用的Web简单页面开发,AI的成功率还是很高的。能准确理解复杂的需求,并将其转化为具体的实现方案。特别是在地图应用这种涉及多个技术栈的项目中,AI能够快速理解前后端的协作关系。从项目初始化到功能完成,整个过程非常流畅。AI能够快速生成代码,大大缩短了开发时间。

对于初学者,因为AI对OpenLayers、Vue 3、Flask等技术栈都非常熟悉,能够熟练使用各种API和最佳实践,你看着AI写程序,很快就知道一个含有大量文件的项目,每一步是怎么从Hello-World丰富起来的,看一次胜读十遍书。此外,AI每次提出新的需求或修改意见,AI都能快速响应并调整代码,迭代效率很高。

项目成果

最终,我们成功构建了一个功能完整的地图应用,包含:

  • ✅ 双图层显示(底图+DEM等高线)
  • ✅ 地图交互(缩放、漫游、重置视图)
  • ✅ 经纬度网格显示
  • ✅ 鼠标位置实时显示
  • ✅ 模式选择(漫游/量测)
  • ✅ 量测功能(距离计算,精确到小数点后3位)
  • ✅ 数据查询(SQLite数据库,按日期查询)
  • ✅ 结果可视化(红色圆点显示)
  • ✅ 自动缩放到数据区域
  • ✅ 地名搜索(OSM查询服务,按名称搜索)
  • ✅ 搜索结果可视化(深蓝色半透明圆形)
  • ✅ CORS跨域问题解决(Vite代理)

技术亮点

  1. 坐标转换:正确处理了WGS84(EPSG:4326)到Web Mercator(EPSG:3857)的转换
  2. 事件管理:妥善处理了地图事件的绑定和解绑,避免内存泄漏
  3. 用户体验:实现了实时反馈(如量测距离实时显示)
  4. 错误处理:完善的错误提示和异常处理
  5. 响应式设计:UI布局合理,交互流畅
  6. 第三方API集成:成功集成OSM地名查询服务,解析复杂的POINT格式坐标数据
  7. CORS跨域处理:通过Vite代理优雅地解决了跨域问题,无需修改后端服务
  8. 数据可视化:使用不同颜色和样式的标记区分不同类型的数据(红色圆点 vs 深蓝色半透明圆形)

注:本文记录了与Trae IDE AI SOLO模式合作开发Vue地图应用的全过程,展示了AI在实际项目开发中的表现和价值。

Read more

ROS导航实战:如何用mpc_local_planner让机器人高效避障(附参数调优技巧)

ROS导航实战:如何用mpc_local_planner让机器人高效避障(附参数调优技巧) 在机器人导航的实战中,局部路径规划器的表现直接决定了机器人在复杂环境下的“驾驶体验”。你是否遇到过机器人面对突然出现的障碍物时犹豫不决,或者转弯时轨迹不够平滑,甚至直接“卡死”在原地的情况?这些问题往往不是机器人硬件的问题,而是局部规划器的选择和调参不当所致。在众多规划器中,mpc_local_planner 凭借其基于模型预测控制(MPC)的优化内核,在处理动态避障和平滑性方面展现出了独特的优势。它不像传统的动态窗口法(DWA)那样只做短视的采样,而是通过预测未来一段时间的轨迹并优化,从而做出更“聪明”的决策。 这篇文章不会重复那些基础的安装和启动步骤,而是直接从实战应用出发,面向那些已经搭建好ROS导航框架,却苦于机器人避障效果不佳的开发者。我们将深入探讨如何配置 mpc_local_planner,特别是针对动态避障场景,分享一系列从踩坑中总结出的参数调优技巧。我会结合具体的Rviz演示效果,对比默认参数与优化参数下的机器人行为差异,并详细解析 costmap_converter 插件

【ROS 2】运行 ROS 2 机器人 ( ROS 2 机器人示例 - 海龟仿真器 | ROS 节点分析工具 - rqt | ros2 run 命令解析 | ros2 run 基础格式和完整格式 )

【ROS 2】运行 ROS 2 机器人 ( ROS 2 机器人示例 - 海龟仿真器 | ROS 节点分析工具 - rqt | ros2 run 命令解析 | ros2 run 基础格式和完整格式 )

文章目录 * 一、ROS 2 机器人示例 - 海龟仿真器 * 1、启动海龟仿真器节点 * 2、启动控制节点 * 3、ROS 节点分析工具 - rqt * 二、ros2 run 命令解析 * 1、设计理念 * 2、ros2 run 基础格式 * 3、ros2 run 完整格式 * 4、启动海龟仿真器命令分析 在上一篇博客 【ROS 2】ROS 2 Humble 完整环境配置 ( VirtualBox 7.2.4 + Ubuntu 22.04.5 LTS + ROS 2

Being-H0.5:扩展以人为中心的机器人学习实现跨具身泛化

Being-H0.5:扩展以人为中心的机器人学习实现跨具身泛化

26年1月来自的BeingBeyond团队的论文“Being-H0.5: Scaling Human-Centric Robot Learning for Cross-Embodiment Generalization”。 Being-H0.5 是一个基础视觉-语言-动作 (VLA) 模型,旨在实现跨不同机器人平台的鲁棒跨具身泛化。现有的 VLA 模型通常难以应对形态异质性和数据稀缺性,而提出的一种以人为中心学习范式,将人类交互痕迹视为物理交互的通用“母语”。为了支持这一范式,推出 UniHand-2.0,这是迄今为止规模最大的具身预训练方案,包含来自 30 种不同机器人具身的超过 35,000 小时多模态数据。该方法引入一个统一动作空间,将异构的机器人控制映射到语义对齐槽中,使低资源机器人能够从人类数据和高资源平台中引导技能。基于这一以人为中心的基础,设计一个统一的序列建模和多任务预训练范式,以连接人类演示和机器人执行。在架构上,Being-H0.5 采用混合 Transformer (MoT)设计,并引入一种混合流 (MoF) 框架,将共享的运动基元与特定于具身的专家解耦。

ROS1机器人SLAM系列(四):Gmapping算法详解与实战

ROS1机器人SLAM系列(四):Gmapping算法详解与实战 本文将深入讲解Gmapping算法的原理,并通过实战演示如何使用Gmapping进行2D激光SLAM建图。 1. Gmapping算法简介 1.1 什么是Gmapping? Gmapping是一种基于**粒子滤波(Rao-Blackwellized Particle Filter, RBPF)**的2D激光SLAM算法。它由Giorgio Grisetti等人于2007年提出,是ROS中最经典、应用最广泛的SLAM算法之一。 主要特点: * 基于粒子滤波的概率框架 * 适用于2D激光雷达 * 需要里程计信息 * 实现成熟,稳定可靠 * 适合中小规模室内环境 1.2 算法流程概述 Gmapping算法流程 里程计数据 运动预测 Motion Model 粒子集合更新 激光雷达数据 扫描匹配 Scan Matching 观测更新 Sensor Model 粒子权重计算 重采样 Resample 地图更新 2. 核心算法原理