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></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></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
- 注释关键逻辑