欢迎光临
我们一直在努力

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

文章目录

  • 一、前言
  • 二、 项目背景
  • 三、交互开发流程
    • 第一次任务:项目初始化
    • 第二次任务:地图交互功能
    • 第三次任务:模式选择与量测功能
    • 第四次任务:数据库查询功能
    • 第五次任务:地名搜索功能
    • 最终任务 应用代码重构和添加注释
      • 重构目标
      • 重构方案
      • 重构成果
      • 重构优势
      • 构建验证
  • 四 AI SOLO模式的优势总结
    • 项目成果
    • 技术亮点

一、前言

上一篇文章里,学会了搭建第一个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立即:

  • 创建了完整的项目结构
  • 配置了Vite构建工具
  • 安装了必要的依赖(Vue 3、OpenLayers 9)
  • 实现了基础的地图显示功能
  • 关键代码片段:

    // 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在理解需求后,迅速实现了这些功能:

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

    // 缩放控制
    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的表现依然出色:

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

    // 量测功能
    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 行,包含了地图初始化、数据查询、地名搜索、量测功能、地图控制等多个功能模块。这种单一文件包含过多职责的代码组织方式存在以下问题:

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

    • 将 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 个文件

    重构优势

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

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

    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代理)

    技术亮点

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

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

    赞(0)
    未经允许不得转载:171主机测评 » 桌面大爷学Web(2)-AI SOLO模式实战:只动嘴不动手从零构建Vue地图页面
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址