桌面大爷学Web(2)-AI SOLO模式实战:只动嘴不动手从零构建Vue地图页面
本文记录了使用Trae IDE的AI SOLO模式开发基于Vue 3和OpenLayers的地图应用的全过程。项目实现了地图显示、交互操作、量测功能和数据库查询等核心功能。AI通过四次主要任务逐步构建应用:初始化项目并集成OpenLayers、添加地图交互功能、实现量测模式切换、集成SQLite数据库查询。在每个任务中,AI都准确理解需求并生成高质量代码,包括前端Vue组件和后端Flask API
文章目录
一、前言
上一篇文章里,学会了搭建第一个Vue应用。本文,通过Trae的SOLO模式,做一个具备简单功能的Web页面。近年来,在软件开发领域,AI辅助编程已经成为一种趋势。最近,老丁体验了Trae IDE的AI SOLO模式,印象挺深!本文记录用AI SOLO开发一个基于Vue 3和OpenLayers的地图应用的全过程,展示AI在实际项目开发中的表现。
二、 项目背景
我们需要开发一个地图应用,主要功能包括:
- 显示本地摩卡托投影瓦片地图
- 支持双图层显示(底图+DEM等高线)
- 提供地图交互功能(缩放、漫游、重置视图等)
- 实现量测功能
- 集成SQLite数据库查询功能
技术栈选择:
- 前端:Vue 3 + Vite + OpenLayers 9
- 后端:Flask + SQLite
其产品界面如下:
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准确理解了量测的交互逻辑,代码实现完整,考虑了各种边界情况(如拖拽时忽略事件)。
第四次任务:数据库查询功能
任务描述:集成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跨域问题。深蓝色半透明圆形标记的样式实现完全符合需求。
最终任务 应用代码重构和添加注释
随着项目功能的不断完善,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)