一、前言

上一篇文章里,学会了搭建第一个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'
import XYZ from 'ol/source/XYZ'

map = new Map({
  target: mapRef.value,
  layers: [
    new TileLayer({
      source: new XYZ({
        url: 'http://127.0.0.1:8087/osm_tiles/{z}/{x}/{y}.png',
        maxZoom: 20,
        minZoom: 0
      })
    })
  ],
  view: new View({
    center: [0, 0],
    zoom: 2,
    maxZoom: 20,
    minZoom: 0
  })
})

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

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

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

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

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

关键实现:

// 缩放控制
const zoomIn = () => {
  const view = map.getView()
  const zoom = view.getZoom()
  view.setZoom(zoom + 1)
}

// 经纬度网格
graticuleLayer = new Graticule({
  strokeStyle: new Stroke({
    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. 交互设计:支持多点量测,双击结束量测,实时显示距离

核心实现:

// 量测功能
const startMeasure = () => {
  isMeasuring.value = true
  measureResult.value = null
  
  const handleClick = (evt) => {
    if (!isDrawing) {
      isDrawing = true
      coordinates = [evt.coordinate]
      const feature = new Feature({
        geometry: new LineString(coordinates)
      })
      sketch = feature
      measureSource.addFeature(feature)
    } else {
      coordinates.push(evt.coordinate)
      if (sketch) {
        sketch.getGeometry().setCoordinates(coordinates)
      }
    }
  }
  
  const handleDoubleClick = (evt) => {
    if (isDrawing) {
      isDrawing = false
      const geometry = sketch.getGeometry()
      const output = formatLength(geometry)
      measureResult.value = output
      stopMeasure()
    }
  }
}

const formatLength = (line) => {
  const length = getLength(line, { projection: 'EPSG:3857' })
  const kilometers = length / 1000
  return kilometers.toFixed(3)
}

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

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

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

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

后端实现(Flask):

@app.route('/api/query_by_date', methods=['GET'])
def query_by_date():
    date_str = request.args.get('date')
    
    if not date_str:
        return jsonify({'error': '请提供日期参数'}), 400
    
    try:
        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
    })

前端实现

const queryData = async () => {
  if (!selectedDate.value) {
    alert('请选择日期')
    return
  }

  try {
    clearTestLayer()

    const response = await fetch(`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 = new Feature({
        geometry: new Point(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的解决方案非常专业:

前端实现

const searchPlaceByName = async () => {
  if (!searchPlace.value) {
    alert('请输入地名')
    return
  }

  try {
    clearSearchPlaceLayer()

    const encodedName = encodeURIComponent(searchPlace.value)
    const response = await fetch(`/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 = new Feature({
            geometry: new Point(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('查询失败,请检查服务器是否运行')
  }
}

const createSearchPlaceLayer = () => {
  const vectorSource = new VectorSource()
  searchPlaceLayer = new VectorLayer({
    source: vectorSource,
    style: new Style({
      image: new Circle({
        radius: 7,
        fill: new Fill({
          color: 'rgba(0, 0, 139, 0.5)'
        })
      })
    })
  })
  map.addLayer(searchPlaceLayer)
}

const parseCenterPos = (centerPos) => {
  const match = centerPos.match(/POINT\(([\d.]+) ([\d.]+)\)/)
  if (match) {
    return {
      lon: parseFloat(match[1]),
      lat: parseFloat(match[2])
    }
  }
  return null
}

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

// vite.config.js
export default defineConfig({
  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.vue 754 行 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 in 1.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在实际项目开发中的表现和价值。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐