核心功能详解
AI 水务前端核心功能详解
一、地图系统
1. 地图组件(DemoMap.vue)
基于 OpenLayers 实现的地图组件:
import Map from 'ol/Map'
import View from 'ol/View'
import TileLayer from 'ol/layer/Tile'
import OSM from 'ol/source/OSM'
import { fromLonLat } from 'ol/proj'
// 创建地图实例
const map = new Map({
target: 'map',
layers: [
new TileLayer({
source: new OSM(),
properties: { name: 'OpenStreetMap', isBase: true }
})
],
view: new View({
center: fromLonLat([116.4074, 39.9042]), // 北京坐标
zoom: 10
})
})
2. 地图实例管理(mapInstance.js)
全局地图实例管理:
// mapInstance.js
export let mapInstance = null
export function setMap(map) {
mapInstance = map
}
export function getMap() {
return mapInstance
}
使用方式:
import { mapInstance, setMap } from './mapInstance.js'
// 设置地图实例
setMap(map)
// 在其他地方使用
const map = mapInstance
map.addLayer(newLayer)
3. 坐标转换
OpenLayers 默认使用 EPSG:3857 投影,需要进行坐标转换:
import { fromLonLat, toLonLat } from 'ol/proj'
// 经纬度转投影坐标
const mapCoord = fromLonLat([116.4074, 39.9042])
// 投影坐标转经纬度
const lonLat = toLonLat([12914834, 4865942])
二、GeoJSON 服务
1. GeoJSON 数据提取
从 AI 响应中提取 GeoJSON 数据:
// geoJsonService.js
export function extractGeoJsonFromResponse(responseText) {
if (!responseText || typeof responseText !== 'string') return null
try {
let parsedData = JSON.parse(responseText)
let geoJson = []
if (parsedData?.data) {
const content = parsedData.data
const parsed = JSON.parse(content)
const arr = Array.isArray(parsed) ? parsed : parsed != null ? [parsed] : []
for (const item of arr) {
const geom = item.geom
geoJson.push(JSON.parse(geom))
}
}
return geoJson
} catch (error) {
console.warn('提取GeoJSON时出错:', error)
return null
}
}
2. 在地图上显示 GeoJSON
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import GeoJSON from 'ol/format/GeoJSON'
import { Style, Stroke, Fill, Circle } from 'ol/style'
export function displayGeoJsonOnMap(geoJsonData) {
if (!geoJsonData || !mapInstance) {
console.warn('无法显示GeoJSON: 缺少数据或地图实例')
return null
}
// 标准化为 FeatureCollection
const featureCollection = normalizeToFeatureCollection(geoJsonData)
// 创建矢量数据源
const vectorSource = new VectorSource({
features: new GeoJSON().readFeatures(featureCollection, {
featureProjection: 'EPSG:3857',
dataProjection: 'EPSG:4326'
})
})
// 创建矢量图层
const vectorLayer = new VectorLayer({
source: vectorSource,
style: createHighlightStyle(),
properties: {
name: 'geojson-highlight',
isGeoJsonLayer: true,
timestamp: Date.now()
}
})
// 添加到地图
mapInstance.addLayer(vectorLayer)
// 添加闪烁动画
addBlinkingAnimation(vectorLayer)
// 调整视图
fitViewToGeoJson(vectorSource)
return vectorLayer
}
3. 高亮样式
function createHighlightStyle() {
return new Style({
stroke: new Stroke({
color: '#ff4444',
width: 3,
lineDash: [5, 5]
}),
fill: new Fill({
color: 'rgba(255, 68, 68, 0.2)'
}),
image: new Circle({
radius: 8,
stroke: new Stroke({
color: '#ff4444',
width: 3
}),
fill: new Fill({
color: 'rgba(255, 68, 68, 0.3)'
})
})
})
}
4. 闪烁动画效果
function addBlinkingAnimation(layer) {
let opacity = 1
let direction = -0.1
const animate = () => {
opacity += direction
if (opacity <= 0.3) {
opacity = 0.3
direction = 0.1
} else if (opacity >= 1) {
opacity = 1
direction = -0.1
}
layer.setOpacity(opacity)
// 持续闪烁5秒
if (Date.now() - layer.get('timestamp') < 5000) {
requestAnimationFrame(animate)
} else {
layer.setOpacity(1)
}
}
requestAnimationFrame(animate)
}
5. 清除 GeoJSON 图层
export function clearGeoJsonLayers() {
if (!mapInstance) return
const layersToRemove = []
mapInstance.getLayers().forEach(layer => {
if (layer.get('isGeoJsonLayer')) {
layersToRemove.push(layer)
}
})
layersToRemove.forEach(layer => {
mapInstance.removeLayer(layer)
})
}
三、Dify AI 客户端
1. 发送聊天消息
支持流式和普通两种响应模式:
// difyClient.js
export async function sendChatMessage(query, opt = {}) {
const {
onMessage = () => {}, // 流式增量文本回调
onCompleted = () => {}, // 完成回调
onError = () => {}, // 错误回调
onGeoJsonDetected = () => {},// GeoJSON 检测回调
abortSignal, // 取消信号
inputs = {}, // 自定义变量
stream = true, // 是否使用流式
user = 'web-user' // 用户标识
} = opt
const url = BASE_URL + '/v1/chat-messages'
const body = {
inputs,
query,
response_mode: stream ? 'streaming' : 'blocking',
user
}
// 流式处理
if (stream) {
const resp = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body),
signal: abortSignal
})
const reader = resp.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = ''
let fullResponseText = ''
while (true) {
const { value, done } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
// SSE 解析
// ...处理流式数据
}
}
}
2. 使用示例
import { sendChatMessage } from './services/difyClient'
// 发送消息
sendChatMessage('查询北京市水系', {
onMessage: (chunk) => {
// 处理增量文本
console.log('收到:', chunk)
},
onCompleted: () => {
console.log('完成')
},
onError: (err) => {
console.error('错误:', err)
},
onGeoJsonDetected: (geoJson) => {
// 自动在地图上显示
displayGeoJsonOnMap(geoJson)
}
})
3. 取消请求
const controller = new AbortController()
sendChatMessage('查询...', {
abortSignal: controller.signal,
// ...其他选项
})
// 取消请求
controller.abort()
四、GeoServer 集成
1. REST API 封装
// GeoServerRestApi.js
import axios from 'axios'
const geoserverApi = axios.create({
baseURL: '/geoserver',
timeout: 10000
})
// 获取服务信息
export async function getAbout() {
const response = await geoserverApi.get('/rest/about/status.json')
return response.data
}
// 获取图层列表
export async function getLayers() {
const response = await geoserverApi.get('/rest/layers.json')
return response.data
}
2. 图层管理
// Layers.js
export async function getLayerList() {
const data = await getLayers()
return data.layers?.layer || []
}
export async function getLayerInfo(layerName) {
const response = await geoserverApi.get(`/rest/layers/${layerName}.json`)
return response.data
}
五、面板组件
1. 图层面板(LayerPanel.vue)
功能:
- 显示可用图层列表
- 图层可见性控制
- 图层添加/移除
2. AI 助手面板(AIAssistantPanel.vue)
功能:
- 聊天消息输入
- 流式响应显示
- 历史消息管理
- GeoJSON 自动识别
3. 可调整布局
// App.vue 中的布局调整逻辑
const startVDrag = (e) => {
isDraggingV.value = true
startY.value = e.clientY
startHeight.value = topHeight.value
document.body.classList.add('resizing-v')
}
const startHDrag = (e) => {
isDraggingH.value = true
startX.value = e.clientX
startLeftWidth.value = leftWidth.value
document.body.classList.add('resizing-h')
}
布局状态自动保存:
// 保存到 localStorage
localStorage.setItem('layerPanelHeight', String(topHeight.value))
localStorage.setItem('leftPanelWidth', String(leftWidth.value))
// 读取保存的状态
const storedHeight = parseInt(localStorage.getItem('layerPanelHeight') || '0', 10)
const storedWidth = parseInt(localStorage.getItem('leftPanelWidth') || '0', 10)
六、开发工具
GeoJSON 测试工具
开发模式下自动加载的测试工具:
// geoJsonTestUtils.js
// 在控制台可用的测试方法
// 测试 GeoJSON 渲染
window.testGeoJson = (geoJson) => {
displayGeoJsonOnMap(geoJson)
}
// 清除测试图层
window.clearGeoJsonLayers = () => {
clearGeoJsonLayers()
}
// 测试数据
window.testData = {
point: {
type: 'Point',
coordinates: [116.4074, 39.9042]
},
polygon: {
type: 'Polygon',
coordinates: [[[116.3, 39.8], [116.5, 39.8], [116.5, 40.0], [116.3, 40.0], [116.3, 39.8]]]
}
}
使用方式:
// 浏览器控制台
window.testGeoJson(window.testData.point)
window.testGeoJson(window.testData.polygon)
window.clearGeoJsonLayers()

浙公网安备 33010602011771号