首页
› demos
› ai-water-front
› AI 水务前端开发指南
AI 水务前端开发指南
一、自定义开发
1. 添加新的地图图层
添加矢量图层
import VectorLayer from ' ol/layer/Vector '
import VectorSource from ' ol/source/Vector '
import GeoJSON from ' ol/format/GeoJSON '
import { Style , Stroke , Fill } from ' ol/style '
// 从 GeoJSON 文件加载
const vectorLayer = new VectorLayer ({
source : new VectorSource ({
url : ' /data/your-data.geojson ' ,
format : new GeoJSON ()
}),
style : new Style ({
stroke : new Stroke ({
color : ' #3399CC ' ,
width : 2
}),
fill : new Fill ({
color : ' rgba(51, 153, 204, 0.3) '
})
}),
properties : {
name : ' 自定义图层 ' ,
type : ' vector '
}
})
// 添加到地图
mapInstance . addLayer ( vectorLayer )
添加 WMS 图层
import TileLayer from ' ol/layer/Tile '
import TileWMS from ' ol/source/TileWMS '
const wmsLayer = new TileLayer ({
source : new TileWMS ({
url : ' /geoserver/wms ' ,
params : {
' LAYERS ' : ' workspace:layername ' ,
' TILED ' : true
},
serverType : ' geoserver '
}),
properties : {
name : ' WMS 图层 '
}
})
mapInstance . addLayer ( wmsLayer )
2. 自定义样式
创建样式函数
import { Style , Stroke , Fill , Circle , Icon , Text } from ' ol/style '
// 根据要素属性动态设置样式
const styleFunction = ( feature ) => {
const type = feature . get ( ' type ' )
const styles = {
water : new Style ({
fill : new Fill ({ color : ' rgba(0, 100, 255, 0.5) ' }),
stroke : new Stroke ({ color : ' #0064FF ' , width : 2 })
}),
pipe : new Style ({
stroke : new Stroke ({ color : ' #FF6600 ' , width : 3 })
}),
station : new Style ({
image : new Circle ({
radius : 8 ,
fill : new Fill ({ color : ' #00FF00 ' }),
stroke : new Stroke ({ color : ' #000 ' , width : 2 })
})
})
}
return styles [ type ] || styles . water
}
vectorLayer . setStyle ( styleFunction )
添加标注
const labelStyle = new Style ({
text : new Text ({
font : ' 14px Arial ' ,
text : feature . get ( ' name ' ),
fill : new Fill ({ color : ' #000 ' }),
stroke : new Stroke ({ color : ' #fff ' , width : 2 }),
offsetY : - 20
})
})
3. 地图交互
添加点击事件
import { Select } from ' ol/interaction '
import { click } from ' ol/events/condition '
// 创建选择交互
const selectInteraction = new Select ({
condition : click ,
style : new Style ({
stroke : new Stroke ({ color : ' #FF0000 ' , width : 4 }),
fill : new Fill ({ color : ' rgba(255, 0, 0, 0.2) ' })
})
})
mapInstance . addInteraction ( selectInteraction )
// 监听选择事件
selectInteraction . on ( ' select ' , ( e ) => {
const selectedFeatures = e . selected
if ( selectedFeatures . length > 0 ) {
const feature = selectedFeatures [ 0 ]
console . log ( ' 选中要素: ' , feature . getProperties ())
}
})
添加悬停效果
import { pointerMove } from ' ol/events/condition '
const hoverInteraction = new Select ({
condition : pointerMove ,
style : new Style ({
stroke : new Stroke ({ color : ' #00FF00 ' , width : 3 })
})
})
mapInstance . addInteraction ( hoverInteraction )
4. 扩展 AI 功能
添加自定义指令处理
// services/aiCommandHandler.js
export function handleAICommand ( response ) {
// 解析 AI 响应中的指令
const commands = parseCommands ( response )
commands . forEach ( cmd => {
switch ( cmd . type ) {
case ' zoom ' :
mapInstance . getView (). setZoom ( cmd . level )
break
case ' center ' :
mapInstance . getView (). setCenter ( fromLonLat ( cmd . coords ))
break
case ' addLayer ' :
addLayerByName ( cmd . layerName )
break
case ' highlight ' :
highlightFeatures ( cmd . filter )
break
}
})
}
自定义 Dify 变量
sendChatMessage ( ' 查询水系 ' , {
inputs : {
area : ' 北京市 ' ,
dataType : ' water_system ' ,
format : ' geojson '
},
// ...其他选项
})
二、组件开发
1. 创建新面板组件
<!-- components/panels/MyPanel.vue -->
< template >
<div class= "my-panel" >
<div class= "panel-header" >
<h3> {{ title }} </h3>
</div>
<div class= "panel-content" >
<slot></slot>
</div>
</div>
</ template >
< script setup >
defineProps ({
title : {
type : String ,
default : ' 面板 '
}
})
</ script >
< style scoped >
.my-panel {
height : 100% ;
display : flex ;
flex-direction : column ;
}
.panel-header {
padding : 10px ;
background : #f0f0f0 ;
border-bottom : 1px solid #ddd ;
}
.panel-content {
flex : 1 ;
overflow : auto ;
padding : 10px ;
}
</ style >
2. 创建地图控件组件
<!-- components/controls/ZoomControl.vue -->
< template >
<div class= "zoom-control" >
<button @ click= "zoomIn" > +</button>
<span> {{ currentZoom }} </span>
<button @ click= "zoomOut" > -</button>
</div>
</ template >
< script setup >
import { ref , onMounted , onUnmounted } from ' vue '
import { mapInstance } from ' @/mapInstance '
const currentZoom = ref ( 10 )
const zoomIn = () => {
const view = mapInstance . getView ()
view . setZoom ( view . getZoom () + 1 )
}
const zoomOut = () => {
const view = mapInstance . getView ()
view . setZoom ( view . getZoom () - 1 )
}
const updateZoom = () => {
currentZoom . value = Math . round ( mapInstance . getView (). getZoom ())
}
onMounted (() => {
mapInstance . getView (). on ( ' change:resolution ' , updateZoom )
})
onUnmounted (() => {
mapInstance . getView (). un ( ' change:resolution ' , updateZoom )
})
</ script >
三、GeoServer 扩展
1. 添加新的 GeoServer 服务
// geoserver/FeatureService.js
import axios from ' axios '
const geoserverApi = axios . create ({
baseURL : ' /geoserver '
})
// WFS 查询
export async function queryFeatures ( typeName , filter ) {
const params = {
service : ' WFS ' ,
version : ' 2.0.0 ' ,
request : ' GetFeature ' ,
typeName ,
outputFormat : ' application/json '
}
if ( filter ) {
params . CQL_FILTER = filter
}
const response = await geoserverApi . get ( ' /wfs ' , { params })
return response . data
}
// 空间查询
export async function queryByBbox ( typeName , bbox ) {
const filter = `BBOX(geom, ${ bbox . join ( ' , ' )} )`
return queryFeatures ( typeName , filter )
}
2. 图层样式管理
// geoserver/StyleService.js
export async function getStyles () {
const response = await geoserverApi . get ( ' /rest/styles.json ' )
return response . data . styles ?. style || []
}
export async function getStyleContent ( styleName ) {
const response = await geoserverApi . get ( `/rest/styles/ ${ styleName } .sld` )
return response . data
}
四、数据处理
1. GeoJSON 数据转换
// utils/geoUtils.js
import { transform } from ' ol/proj '
// 坐标转换
export function transformCoordinates ( coords , from , to ) {
if ( Array . isArray ( coords [ 0 ])) {
return coords . map ( c => transformCoordinates ( c , from , to ))
}
return transform ( coords , from , to )
}
// GeoJSON 投影转换
export function transformGeoJson ( geojson , fromProj , toProj ) {
const transformed = JSON . parse ( JSON . stringify ( geojson ))
if ( transformed . type === ' FeatureCollection ' ) {
transformed . features . forEach ( feature => {
feature . geometry . coordinates = transformCoordinates (
feature . geometry . coordinates ,
fromProj ,
toProj
)
})
} else if ( transformed . type === ' Feature ' ) {
transformed . geometry . coordinates = transformCoordinates (
transformed . geometry . coordinates ,
fromProj ,
toProj
)
}
return transformed
}
2. 数据验证
// utils/validation.js
export function isValidGeoJSON ( data ) {
if ( ! data || typeof data !== ' object ' ) return false
const validTypes = [
' Point ' , ' LineString ' , ' Polygon ' ,
' MultiPoint ' , ' MultiLineString ' , ' MultiPolygon ' ,
' Feature ' , ' FeatureCollection '
]
if ( ! validTypes . includes ( data . type )) return false
if ( data . type === ' Feature ' ) {
return data . geometry && data . geometry . coordinates
}
if ( data . type === ' FeatureCollection ' ) {
return Array . isArray ( data . features )
}
return data . coordinates !== undefined
}
五、最佳实践
1. 组件设计原则
单一职责 :每个组件只负责一个功能
可复用性 :通过 props 和 events 实现组件复用
解耦合 :地图逻辑与 UI 组件分离
2. 性能优化
大量要素使用聚合或简化
图层按需加载
使用 Web Worker 处理复杂计算
合理使用缓存
3. 错误处理
// 统一错误处理
export function handleError ( error , context = '' ) {
console . error ( `[ ${ context } ]` , error )
// 可以添加用户提示
// showNotification({ type: 'error', message: error.message })
// 可以上报错误
// reportError(error, context)
}
4. 代码规范
使用 ESLint 进行代码检查
组件命名使用 PascalCase
文件命名使用 camelCase 或 kebab-case
注释关键逻辑
六、学习资源
OpenLayers
GeoServer
Dify
Vue 3