From e17a73537cbd11ae641b58b651f832eecdbbc46c Mon Sep 17 00:00:00 2001
From: wzy-warehouse <18135009705@163.com>
Date: Sat, 11 Apr 2026 10:09:40 +0800
Subject: [PATCH] =?UTF-8?q?=E9=9A=90=E6=82=A3=E7=82=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
components.d.ts | 5 +
package.json | 2 +-
pnpm-lock.yaml | 2 +-
src/App.vue | 15 ++-
src/api/api.ts | 13 ++
src/api/crypto.ts | 4 +-
src/api/hidden-danger-spots.ts | 26 ++++
src/assets/images/icon/debris-flow.png | Bin 0 -> 4715 bytes
src/assets/images/icon/flash-flood.png | Bin 0 -> 4164 bytes
src/assets/images/icon/landslide.png | Bin 0 -> 3627 bytes
src/assets/images/icon/waterlogging.png | Bin 0 -> 3246 bytes
src/component/common/InformationBox.vue | 110 ++++++++++++++++
src/component/map/Map.vue | 20 ++-
.../rain-earthquake/BasicComponent.vue | 26 ++++
.../rain-earthquake/HiddenPointComponent.vue | 120 ++++++++++++++++++
.../rain-earthquake/LoadingPoints.vue | 26 ++++
src/config/config.json | 7 +-
src/hooks/usePointsHandle.ts | 65 ++++++++++
src/stores/useViewerStore.ts | 15 +++
src/types/{Response.ts => ApiResponse.ts} | 2 +-
src/types/base/Point.ts | 12 ++
src/types/base/XianHiddenDangerSpots.ts | 33 +++++
src/types/cesium/PrimitiveOptions.ts | 4 +-
src/types/common/DisasterType.ts | 4 +
src/utils/cesium/CesiumUtils.ts | 28 +++-
src/utils/cesium/PrimitiveManager.ts | 16 ++-
src/utils/utils.ts | 25 ++++
src/views/home/earthquake/Earthquake.vue | 9 +-
src/views/home/rainstorm/Rainstorm.vue | 9 +-
29 files changed, 573 insertions(+), 25 deletions(-)
create mode 100644 src/api/hidden-danger-spots.ts
create mode 100644 src/assets/images/icon/debris-flow.png
create mode 100644 src/assets/images/icon/flash-flood.png
create mode 100644 src/assets/images/icon/landslide.png
create mode 100644 src/assets/images/icon/waterlogging.png
create mode 100644 src/component/common/InformationBox.vue
create mode 100644 src/component/rain-earthquake/BasicComponent.vue
create mode 100644 src/component/rain-earthquake/HiddenPointComponent.vue
create mode 100644 src/component/rain-earthquake/LoadingPoints.vue
create mode 100644 src/hooks/usePointsHandle.ts
create mode 100644 src/stores/useViewerStore.ts
rename src/types/{Response.ts => ApiResponse.ts} (50%)
create mode 100644 src/types/base/Point.ts
create mode 100644 src/types/base/XianHiddenDangerSpots.ts
create mode 100644 src/types/common/DisasterType.ts
diff --git a/components.d.ts b/components.d.ts
index 227ab48..65dc19f 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -11,6 +11,11 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
+ ElButton: typeof import('element-plus/es')['ElButton']
+ ElDialog: typeof import('element-plus/es')['ElDialog']
+ ElIcon: typeof import('element-plus/es')['ElIcon']
+ ElTable: typeof import('element-plus/es')['ElTable']
+ ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
diff --git a/package.json b/package.json
index 12b5401..70d8acb 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
"@types/spark-md5": "^3.0.5",
"axios": "^1.12.2",
"cesium": "1.101.0",
- "element-plus": "^2.11.7",
+ "element-plus": "^2.13.6",
"gm-crypto": "^0.1.12",
"pinia": "^3.0.3",
"spark-md5": "^3.0.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1bea220..88e70bb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -21,7 +21,7 @@ importers:
specifier: 1.101.0
version: 1.101.0
element-plus:
- specifier: ^2.11.7
+ specifier: ^2.13.6
version: 2.13.6(typescript@6.0.2)(vue@3.5.32(typescript@6.0.2))
gm-crypto:
specifier: ^0.1.12
diff --git a/src/App.vue b/src/App.vue
index 6078adf..c3b7c92 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,8 +2,21 @@
-
\ No newline at end of file
diff --git a/src/component/map/Map.vue b/src/component/map/Map.vue
index 18caa67..932dbdf 100644
--- a/src/component/map/Map.vue
+++ b/src/component/map/Map.vue
@@ -2,23 +2,33 @@
-
+
+
+
diff --git a/src/component/rain-earthquake/HiddenPointComponent.vue b/src/component/rain-earthquake/HiddenPointComponent.vue
new file mode 100644
index 0000000..ce3d734
--- /dev/null
+++ b/src/component/rain-earthquake/HiddenPointComponent.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/component/rain-earthquake/LoadingPoints.vue b/src/component/rain-earthquake/LoadingPoints.vue
new file mode 100644
index 0000000..d0b0602
--- /dev/null
+++ b/src/component/rain-earthquake/LoadingPoints.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/src/config/config.json b/src/config/config.json
index 4f00fdf..2625359 100644
--- a/src/config/config.json
+++ b/src/config/config.json
@@ -12,5 +12,10 @@
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ZDBjZjAxOS0wMDhhLTRmZjEtYjNmOC1iNmM2ZmY2ZmQ1N2IiLCJpZCI6MjAxMDI1LCJpYXQiOjE3MTAxNTgxNjJ9.mdbJYEzXQkBnHNqpozz7MvZjJ_X9a3JZRGPA-ytGhLI",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjczZTVlMy1kNDEwLTRhZWItYWM0NS1mNjYxMzJjODMwYTQiLCJpZCI6MzIxMzI2LCJpYXQiOjE3NzU2NDU1OTd9._MPcZQsxK1dGPl8IMVhKHV3PIPu4-TaOUgzsUUOP6WE"
],
- "defaultPosition": [108.948024, 34.263161, 200000]
+ "defaultPosition": [108.948024, 34.263161, 300000],
+
+ "prefix": {
+ "hiddenDangerPointId": "hidden-danger-point-",
+ "riskPointId": "risk-point-"
+ }
}
diff --git a/src/hooks/usePointsHandle.ts b/src/hooks/usePointsHandle.ts
new file mode 100644
index 0000000..3375f77
--- /dev/null
+++ b/src/hooks/usePointsHandle.ts
@@ -0,0 +1,65 @@
+import type { Point } from "@/types/base/Point";
+import type { PrimitiveOptions } from "@/types/cesium/PrimitiveOptions";
+import { CesiumUtilsSingleton } from "@/utils/cesium/CesiumUtils";
+import { Cartesian3, HorizontalOrigin, NearFarScalar, VerticalOrigin } from "cesium";
+import config from '@/config/config.json'
+
+/**
+ * 公共批量处理点钩子函数
+ */
+export const usePointsHandle = () => {
+
+
+ /**
+ * 添加点
+ * @param points - 点数据
+ * @param getDisasterIcon - 获取灾害图标的函数
+ * @returns 点的ID列表
+ */
+ function addPoints(points: Point[], getDisasterIcon: (disasterType: string) => string): string[] {
+ // 设置加载配置
+ const options: PrimitiveOptions[] = [];
+
+ // 存放id
+ const ids: string[] = [];
+
+ points.forEach(point => {
+ try {
+
+ if (point.lon === undefined || point.lat === undefined || point.disasterType === undefined) {
+ throw new Error(`点位数据缺少经纬度或者灾害类型:${point.id}`);
+ }
+ // 将经纬度转换为笛卡尔坐标
+ const position = Cartesian3.fromDegrees(point.lon, point.lat, 0);
+
+ const id = `${config.prefix.hiddenDangerPointId}${point.id}`
+ ids.push(id)
+
+ options.push({
+ id: id,
+ type: 'billboard',
+ positions: [position],
+ image: getDisasterIcon(point.disasterType),
+ scale: 0.8,
+ width: 40,
+ isDefault: false,
+ scaleByDistance: new NearFarScalar(500, 1, 5e5, 0.3),
+ customProperties: {
+ verticalOrigin: VerticalOrigin.BOTTOM,
+ horizontalOrigin: HorizontalOrigin.CENTER,
+ height: 40
+ },
+ });
+ } catch (error) {
+ throw new Error(`处理点位失败:${point.id}`);
+ }
+ })
+
+ // 批量创建图层
+ CesiumUtilsSingleton.addPrimitivesBatch(options);
+
+ return ids
+ }
+
+ return { addPoints}
+}
\ No newline at end of file
diff --git a/src/stores/useViewerStore.ts b/src/stores/useViewerStore.ts
new file mode 100644
index 0000000..ee25c03
--- /dev/null
+++ b/src/stores/useViewerStore.ts
@@ -0,0 +1,15 @@
+import { defineStore } from 'pinia'
+import { type Ref, ref } from 'vue'
+
+export const useViewerStore = defineStore('viewer', () => {
+ // viewer完成状态
+ const viewerLoadingCompleted: Ref = ref(false)
+
+ // get/set方法
+ const getViewerLoadingCompleted = () => viewerLoadingCompleted.value
+ const setViewerLoadingCompleted = (value: boolean) => {
+ viewerLoadingCompleted.value = value
+ }
+
+ return { getViewerLoadingCompleted, setViewerLoadingCompleted }
+})
diff --git a/src/types/Response.ts b/src/types/ApiResponse.ts
similarity index 50%
rename from src/types/Response.ts
rename to src/types/ApiResponse.ts
index 499be55..3e0e56b 100644
--- a/src/types/Response.ts
+++ b/src/types/ApiResponse.ts
@@ -1,4 +1,4 @@
-export interface Response {
+export interface ApiResponse {
code: number
message: string
data: T
diff --git a/src/types/base/Point.ts b/src/types/base/Point.ts
new file mode 100644
index 0000000..b7624e8
--- /dev/null
+++ b/src/types/base/Point.ts
@@ -0,0 +1,12 @@
+export interface Point {
+ /** 序号 */
+ id?: number;
+ /** 经度 */
+ lon?: number;
+ /** 纬度 */
+ lat?: number;
+ /** 空间 */
+ geom?: string;
+ /** 灾害类型 */
+ disasterType?: string;
+}
\ No newline at end of file
diff --git a/src/types/base/XianHiddenDangerSpots.ts b/src/types/base/XianHiddenDangerSpots.ts
new file mode 100644
index 0000000..53789f4
--- /dev/null
+++ b/src/types/base/XianHiddenDangerSpots.ts
@@ -0,0 +1,33 @@
+import type { Point } from "./Point";
+
+/**
+ * 地质灾害隐患点
+ */
+export interface XianHiddenDangerSpots extends Point{
+ /** 野外编号 */
+ fieldCode?: string;
+ /** 省 */
+ province?: string;
+ /** 省编号 */
+ provinceId?: string;
+ /** 市 */
+ city?: string;
+ /** 市编号 */
+ cityId?: string;
+ /** 县 */
+ county?: string;
+ /** 县编号 */
+ countyId?: string;
+ /** 乡镇 */
+ village?: string;
+ /** 灾害点名称 */
+ disasterName?: string;
+ /** 位置 */
+ position?: string;
+ /** 规模等级 */
+ scaleGrade?: string;
+ /** 险情等级 */
+ riskGrade?: string;
+ /** 逻辑删除标识,0未删除,1已删除 */
+ isDelete?: 0 | 1;
+}
\ No newline at end of file
diff --git a/src/types/cesium/PrimitiveOptions.ts b/src/types/cesium/PrimitiveOptions.ts
index be6fa42..0056e5f 100644
--- a/src/types/cesium/PrimitiveOptions.ts
+++ b/src/types/cesium/PrimitiveOptions.ts
@@ -1,4 +1,4 @@
-import type { Cartesian3, Color } from 'cesium'
+import type { Cartesian3, Color, NearFarScalar } from 'cesium'
export interface PrimitiveOptions {
id: string
@@ -10,4 +10,6 @@ export interface PrimitiveOptions {
width?: number // 线宽
image?: string // 广告牌图片
scale?: number // 广告牌缩放
+ scaleByDistance?: NearFarScalar // 广告牌距离衰减缩放
+ customProperties?: Record // 自定义属性对象
}
diff --git a/src/types/common/DisasterType.ts b/src/types/common/DisasterType.ts
new file mode 100644
index 0000000..f4cbe01
--- /dev/null
+++ b/src/types/common/DisasterType.ts
@@ -0,0 +1,4 @@
+export enum DisasterType {
+ RAINSTORM = 'rainstorm',
+ EARTHQUAKE= 'earthquake'
+}
\ No newline at end of file
diff --git a/src/utils/cesium/CesiumUtils.ts b/src/utils/cesium/CesiumUtils.ts
index c99c3e6..f7e371c 100644
--- a/src/utils/cesium/CesiumUtils.ts
+++ b/src/utils/cesium/CesiumUtils.ts
@@ -3,7 +3,7 @@ import type { EntityOptions } from '@/types/cesium/EntityOptions'
import type { PrimitiveOptions } from '@/types/cesium/PrimitiveOptions'
import type { LayerConfig } from '@/types/cesium/LayerConfig'
import type { CustomizeGeoJsonDataSource, GeoJsonOptions } from '@/types/cesium/GeoJsonOptions'
-import { Viewer, Entity, DataSource, ImageryLayer, Primitive, BillboardCollection, Cartesian3 } from 'cesium'
+import { Viewer, Entity, DataSource, ImageryLayer, Primitive, BillboardCollection, Cartesian3, ScreenSpaceEventHandler, ScreenSpaceEventType, Cartesian2, SceneTransforms } from 'cesium'
import { CesiumViewerManager } from './CesiumViewerManager'
import { EntityManager } from './EntityManager'
import { PrimitiveManager } from './PrimitiveManager'
@@ -38,7 +38,7 @@ export class CesiumUtils {
*/
initCesiumViewer(options: CesiumInitOptions, tdMapToken?: string[], type: number = 0): void {
this.#viewerManager.initCesiumViewer(options, tdMapToken, type)
-
+
const viewer = this.#viewerManager.getViewer()
if (viewer) {
this.#entityManager = new EntityManager(viewer)
@@ -401,6 +401,20 @@ export class CesiumUtils {
return this.#geoJsonManager!.getGeoJsonLayerIds(clearType)
}
+ // ===================== 图层操作 =====================
+ /**
+ * 监听点击事件
+ */
+ clickLayer(callback: (pickedObject: object) => void) {
+ const handler = new ScreenSpaceEventHandler(this.getViewer()?.scene.canvas);
+ handler.setInputAction((clickEvent: {position: Cartesian2}) => {
+ // 在点击位置进行拾取
+ const pickedObject = CesiumUtilsSingleton.getViewer()?.scene.pick(clickEvent.position);
+
+ callback(pickedObject);
+ }, ScreenSpaceEventType.LEFT_CLICK);
+ }
+
// ===================== 视角控制 =====================
/**
@@ -465,6 +479,16 @@ export class CesiumUtils {
return positions.map((pos) => this.convertPosition(pos))
}
+ /**
+ * 将Cartesian3坐标转换为屏幕坐标
+ * @param pos 坐标
+ * @returns 偏移量
+ */
+ convertScreenPosition(pos: Cartesian3): {x: number, y: number} {
+ const windowCoord = SceneTransforms.wgs84ToWindowCoordinates(this.getViewer()!.scene, pos);
+ return {x: windowCoord.x, y: windowCoord.y}
+ }
+
// ===================== 私有方法 =====================
/**
diff --git a/src/utils/cesium/PrimitiveManager.ts b/src/utils/cesium/PrimitiveManager.ts
index 14a7379..fa1feb7 100644
--- a/src/utils/cesium/PrimitiveManager.ts
+++ b/src/utils/cesium/PrimitiveManager.ts
@@ -14,6 +14,7 @@ import {
BillboardGraphics,
VerticalOrigin,
HorizontalOrigin,
+ NearFarScalar,
} from 'cesium'
import type { PrimitiveOptions } from '@/types/cesium/PrimitiveOptions'
import type { Viewer } from 'cesium'
@@ -259,13 +260,24 @@ export class PrimitiveManager {
options.forEach((option) => {
const position = this.#convertPosition(option.positions[0]!)
- collection.add({
+
+ const billboardConfig: any = {
id: option.id,
position,
image: option.image,
scale: option.scale || 1,
color: option.color || Color.WHITE,
- })
+ }
+
+ if (option.scaleByDistance) {
+ billboardConfig.scaleByDistance = option.scaleByDistance
+ }
+
+ if (option.customProperties) {
+ Object.assign(billboardConfig, option.customProperties)
+ }
+
+ collection.add(billboardConfig)
})
this.#viewer.scene.primitives.add(collection)
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 3dd5342..37befa9 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -80,6 +80,7 @@ export const Utils = {
return result.replace(regex, String(value ?? ''))
}, format)
},
+
/**
* 深拷贝函数
* 支持类型:原始类型、数组、对象、Date、RegExp、Map、Set、ArrayBuffer等
@@ -200,4 +201,28 @@ export const Utils = {
// 对于其他无法处理的情况,返回原值
return source
},
+
+ /**
+ * 调整元素位置,确保其不超出屏幕可视区域边界
+ * @param {number} offsetX - 元素左上角原 X 坐标(相对于视口)
+ * @param {number} offsetY - 元素左上角原 Y 坐标(相对于视口)
+ * @param {number} width - 元素的宽度
+ * @param {number} height - 元素的高度
+ * @returns {[number, number]} 调整后的 [newX, newY]
+ */
+ keepWithinScreen: (offsetX: number, offsetY: number, width: number, height: number) => {
+ const viewportW = window.innerWidth;
+ const viewportH = window.innerHeight;
+
+ // 计算允许的 X 范围:最小 0,最大 (视口宽度 - 元素宽度)
+ // 如果元素宽度大于视口宽度,允许范围为 [0, 0](即只能左对齐)
+ const maxX = Math.max(0, viewportW - width);
+ const newX = Math.min(Math.max(offsetX, 0), maxX);
+
+ // 计算允许的 Y 范围
+ const maxY = Math.max(0, viewportH - height);
+ const newY = Math.min(Math.max(offsetY, 0), maxY);
+
+ return [newX, newY];
+ }
}
diff --git a/src/views/home/earthquake/Earthquake.vue b/src/views/home/earthquake/Earthquake.vue
index 3eaa73f..c006cab 100644
--- a/src/views/home/earthquake/Earthquake.vue
+++ b/src/views/home/earthquake/Earthquake.vue
@@ -1,9 +1,12 @@
- 地震灾害链
+
+
-
+
+
diff --git a/src/views/home/rainstorm/Rainstorm.vue b/src/views/home/rainstorm/Rainstorm.vue
index 9561cf3..5a3d38a 100644
--- a/src/views/home/rainstorm/Rainstorm.vue
+++ b/src/views/home/rainstorm/Rainstorm.vue
@@ -1,13 +1,12 @@
-
-
+
-
+