iot对接

This commit is contained in:
jiang 2025-09-14 17:34:45 +08:00
parent 2390903198
commit 46277a4f39
2 changed files with 292 additions and 121 deletions

View File

@ -1,91 +1,224 @@
// 坐标转换工具函数
const xPI = (Math.PI * 3000.0) / 180.0
const PI = Math.PI
const a = 6378245.0
const ee = 0.00669342162296594323
// 坐标转换常量定义
const xPI = (Math.PI * 3000.0) / 180.0;
const PI = Math.PI;
const a = 6378245.0; // 长半轴
const ee = 0.00669342162296594323; // 扁率
/**
*
* @param lng
* @param lat
* @returns
*/
function transformlat(lng: number, lat: number): number {
let ret =
-100.0 +
2.0 * lng +
3.0 * lat +
0.2 * lat * lat +
0.1 * lng * lat +
0.2 * Math.sqrt(Math.abs(lng))
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0
ret += ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / 3.0
ret += ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * 2.0) / 3.0
return ret
}
function transformlng(lng: number, lat: number): number {
let ret =
300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0
ret += ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / 3.0
ret += ((150.0 * Math.sin((lng / 12.0) * PI) + 300.0 * Math.sin((lng / 30.0) * PI)) * 2.0) / 3.0
return ret
}
function convertDmToDd(dmValue: any) {
// 确保是字符串类型
const str = dmValue.toString()
// 经度处理(dddmm.mmmm)
if (str.length > 11) {
const degrees = parseFloat(str.substring(0, 3))
const minutes = parseFloat(str.substring(3))
return degrees + minutes / 60
}
// 纬度处理(ddmm.mmmm)
else {
const degrees = parseFloat(str.substring(0, 2))
const minutes = parseFloat(str.substring(2))
return degrees + minutes / 60
}
}
interface CoordInfo {
[key: string]: any
latitude: number
longitude: number
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0;
ret += ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / 3.0;
ret += ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * 2.0) / 3.0;
return ret;
}
/**
* WGS84 -> GCJ02 -> BD09
* @param lng
* @param lat
* @param extra
* @returns
*
* @param lng
* @param lat
* @returns
*/
export function wgs84ToGcj02ToBd09Public(
lng: number,
lat: number,
extra: { [key: string]: any } = {},
): CoordInfo {
lng = convertDmToDd(lng)
lat = convertDmToDd(lat)
// Step 1: WGS84 -> GCJ02
let dlat = transformlat(lng - 105.0, lat - 35.0)
let dlng = transformlng(lng - 105.0, lat - 35.0)
let radlat = (lat / 180.0) * PI
let magic = Math.sin(radlat)
magic = 1 - ee * magic * magic
let sqrtmagic = Math.sqrt(magic)
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
let mglat = lat + dlat
let mglng = lng + dlng
function transformlng(lng: number, lat: number): number {
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0;
ret += ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / 3.0;
ret += ((150.0 * Math.sin((lng / 12.0) * PI) + 300.0 * Math.sin((lng / 30.0) * PI)) * 2.0) / 3.0;
return ret;
}
// Step 2: GCJ02 -> BD09
let z = Math.sqrt(mglng * mglng + mglat * mglat) + 0.00002 * Math.sin(mglat * xPI)
let theta = Math.atan2(mglat, mglng) + 0.000003 * Math.cos(mglng * xPI)
let bdlng = z * Math.cos(theta) + 0.0065
let bdlat = z * Math.sin(theta) + 0.006
/**
* (DM)(DD)
* @param dmValue
* @param isLongitude true为经度false为纬度
* @returns null
*/
function convertDmToDd(dmValue: string | number, isLongitude: boolean): number | null {
try {
// 确保是字符串类型并去除空白
const str = dmValue.toString().trim();
return {
...extra,
longitude: bdlng,
latitude: bdlat,
// 验证输入是否为有效的数字字符串
if (!/^\d+(\.\d+)?$/.test(str)) {
throw new Error(`无效的度分格式: ${str}`);
}
// 根据经纬度类型确定度的位数
const degreeDigits = isLongitude ? 3 : 2;
// 处理字符串长度不足的情况
if (str.length < degreeDigits) {
throw new Error(`度分格式字符串过短: ${str}`);
}
// 提取度和分
const degrees = parseFloat(str.substring(0, degreeDigits));
const minutes = parseFloat(str.substring(degreeDigits));
// 验证度和分的范围
if (isLongitude && (degrees < 0 || degrees > 180)) {
throw new Error(`无效的经度度数: ${degrees}`);
}
if (!isLongitude && (degrees < 0 || degrees > 90)) {
throw new Error(`无效的纬度度数: ${degrees}`);
}
if (minutes < 0 || minutes >= 60) {
throw new Error(`无效的分数值: ${minutes}`);
}
return degrees + minutes / 60;
} catch (error) {
console.error("度分转十进制度失败:", error);
return null;
}
}
/**
*
*/
interface CoordInfo {
[key: string]: any;
latitude: number; // 纬度
longitude: number; // 经度
success?: boolean; // 转换是否成功
error?: string; // 错误信息(如果转换失败)
}
/**
* WGS84转GCJ02坐标系
* @param lng
* @param lat
* @returns GCJ02坐标
*/
function wgs84ToGcj02(lng: number, lat: number): { lng: number, lat: number } {
// 如果不在中国境内,直接返回原坐标
if (outOfChina(lng, lat)) {
return { lng, lat };
}
let dlat = transformlat(lng - 105.0, lat - 35.0);
let dlng = transformlng(lng - 105.0, lat - 35.0);
let radlat = (lat / 180.0) * PI;
let magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
let sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
return {
lng: lng + dlng,
lat: lat + dlat
};
}
/**
* GCJ02转BD09坐标系
* @param lng
* @param lat
* @returns BD09坐标
*/
function gcj02ToBd09(lng: number, lat: number): { lng: number, lat: number } {
let z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * xPI);
let theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * xPI);
return {
lng: z * Math.cos(theta) + 0.0065,
lat: z * Math.sin(theta) + 0.006
};
}
/**
*
* @param lng
* @param lat
* @returns
*/
function outOfChina(lng: number, lat: number): boolean {
return (lng < 73.66 || lng > 135.05 || lat < 18.16 || lat > 53.55);
}
/**
* WGS84坐标到BD09坐标
* @param lng
* @param lat
* @param extra
* @returns
*/
export function wgs84ToBd09Direct(
lng: number,
lat: number,
extra: { [key: string]: any } = {}
): CoordInfo {
try {
// 先转换为GCJ02
const gcj = wgs84ToGcj02(lng, lat);
// 再转换为BD09
const bd = gcj02ToBd09(gcj.lng, gcj.lat);
return {
...extra,
longitude: bd.lng,
latitude: bd.lat,
success: true
};
} catch (error) {
console.error("坐标转换失败:", error);
return {
...extra,
longitude: 0,
latitude: 0,
success: false,
error: error instanceof Error ? error.message : "未知错误"
};
}
}
/**
* WGS84坐标转换为BD09坐标
* @param lng
* @param lat
* @param extra
* @returns
*/
export function wgs84ToBd09FromDm(
lng: string | number,
lat: string | number,
extra: { [key: string]: any } = {}
): CoordInfo {
try {
// 先将度分格式转换为十进制度
const longitude = convertDmToDd(lng, true);
const latitude = convertDmToDd(lat, false);
if (longitude === null || latitude === null) {
throw new Error("度分格式转换失败");
}
// 再进行坐标系转换
return wgs84ToBd09Direct(longitude, latitude, extra);
} catch (error) {
console.error("坐标转换失败:", error);
return {
...extra,
longitude: 0,
latitude: 0,
success: false,
error: error instanceof Error ? error.message : "未知错误"
};
}
}
// 使用示例
// 1. 转换度分格式的坐标
// const dmResult = wgs84ToBd09FromDm("1131234.56", "224567.89");
// console.log("度分格式转换结果:", dmResult);
// 2. 直接转换十进制度格式的坐标
// const directResult = wgs84ToBd09Direct(113.8292617797852, 22.60650444030762);
// console.log("直接转换结果:", directResult);

View File

@ -9,7 +9,7 @@ import { getHotList } from 'http/api/equip'
import { loginNewApi } from 'http/api/home/index'
import { getUserInfoAPI } from 'http/api/login/index'
import { ElMessage } from 'element-plus'
import { wgs84ToGcj02ToBd09Public } from 'utils/map'
import { wgs84ToBd09Direct } from 'utils/map'
import { getMapData, mapLoginApi, getIotListApi, getBadgeInfoApi } from 'http/api/map'
import icon1 from '@/assets/img/work-card.jpg'
import icon2 from '@/assets/img/IOT.jpg'
@ -30,6 +30,7 @@ const mapWorkCardData: any = ref({}) // 工牌信息数据
let timer: any = ref()
//
const points: any = ref([])
const iot: any = ref([])
const mapData = ref()
const tagData: any = ref()
const tagIndex: any = ref()
@ -60,24 +61,27 @@ const getMapDataList = async () => {
try {
await getMapData()
} catch (res: any) {
console.log('--------------', res)
console.log('🚀 ~ getMapDataList ~ res:', res)
if (res) {
let { longitude, latitude } = wgs84ToGcj02ToBd09Public(res.longitude, res.latitude)
mapData.value = res
mapData.value.longitude = longitude
mapData.value.latitude = latitude
console.log('🚀 ~ getMapDataList ~ longitude:', longitude, latitude)
points.value = [
{
id: 4,
if (res && res.length > 0) {
const point = res.map((item: any) => {
const longitudeNum = Number(item.longitude)
const latitudeNum = Number(item.latitude)
console.log(longitudeNum, latitudeNum)
let { longitude, latitude } = wgs84ToBd09Direct(latitudeNum, longitudeNum)
console.log('🚀 ~ getMapDataList ~ longitude:', longitude, latitude)
return {
id: item.uuid,
lng: longitude,
lat: latitude,
item: item,
isWork: false,
},
]
}
})
points.value = [...JSON.parse(JSON.stringify(points.value)), ...point]
}
getWorkData()
// initMap()
//initMap()
}
}
@ -285,11 +289,12 @@ const initMap = () => {
} else {
// -
mapDialog.value = true
mapData.value = item.item
tagData.value = mapData.value.aphase
tagIndex.value = 1
timer.value = setInterval(() => {
loginApi()
}, 3000)
}, 180000)
}
})
})
@ -358,7 +363,9 @@ onMounted(async () => {
<li class="left-filter" @click="onSelectOptions">
<div>
{{ selectOptionsValue }}
<el-icon style="margin-left: 10px"><ArrowDownBold /></el-icon>
<el-icon style="margin-left: 10px">
<ArrowDownBold />
</el-icon>
</div>
<div class="select-options" v-if="selectOptions">
@ -441,7 +448,7 @@ onMounted(async () => {
font-size: 16px;
letter-spacing: 1px;
"
>更多产品</a
>更多产品</a
>
</div>
@ -511,10 +518,14 @@ onMounted(async () => {
</div>
</template>
<div class="card-item">
<span class="card-title">经度: </span>{{ isNaN(Number(mapData.longitude)) ? '' : mapData.longitude }} N
<span
class="card-title">经度: </span>{{ isNaN(Number(mapData.longitude)) ? '' : mapData.longitude
}} N
</div>
<div class="card-item">
<span class="card-title">维度: </span>{{ isNaN(Number(mapData.latitude)) ? '' : mapData.latitude }} E
<span
class="card-title">维度: </span>{{ isNaN(Number(mapData.latitude)) ? '' : mapData.latitude
}} E
</div>
<div class="card-item">
<span class="card-title">速度: </span>{{ mapData.speedOverGround }}
@ -575,7 +586,10 @@ onMounted(async () => {
</div>
</el-card>
</el-col>
<el-col :span="24" style="margin-bottom: 20px">
<el-col v-show="mapData.aphase !== undefined && mapData.aphase !== null && mapData.aphase !== ''
|| mapData.bphase !== undefined && mapData.bphase !== null && mapData.bphase !== ''
|| mapData.cphase !== undefined && mapData.cphase !== null && mapData.cphase !== ''" :span="24"
style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
@ -583,14 +597,20 @@ onMounted(async () => {
</div>
</template>
<div style="margin-bottom: 15px">
<el-button :type="tagIndex == 1 ? 'primary' : ''" @click="handleTag(1)"
>A组</el-button
<el-button v-show="mapData.aphase !== undefined && mapData.aphase !== null"
:type="tagIndex == 1 ? 'primary' : ''" @click="handleTag(1)"
>A组
</el-button
>
<el-button :type="tagIndex == 2 ? 'primary' : ''" @click="handleTag(2)"
>B组</el-button
<el-button v-show="mapData.bphase !== undefined && mapData.bphase !== null"
:type="tagIndex == 2 ? 'primary' : ''" @click="handleTag(2)"
>B组
</el-button
>
<el-button :type="tagIndex == 3 ? 'primary' : ''" @click="handleTag(3)"
>C组</el-button
<el-button v-show="mapData.cphase !== undefined && mapData.cphase !== null"
:type="tagIndex == 3 ? 'primary' : ''" @click="handleTag(3)"
>C组
</el-button
>
</div>
<div
@ -599,64 +619,64 @@ onMounted(async () => {
>
<div>
<div class="item">
<span class="card-title">电压: </span>{{ tagData.voltage }} W
<span class="card-title">电压: </span>{{ tagData?.voltage }} W
</div>
<div class="item">
<span class="card-title">电流: </span>{{ tagData.current }} A
<span class="card-title">电流: </span>{{ tagData?.current }} A
</div>
<div class="item">
<span class="card-title">有功功率: </span
>{{ tagData.activePower }} V
>{{ tagData?.activePower }} V
</div>
</div>
<div>
<div class="item">
<span class="card-title">无功功率: </span
>{{ tagData.reactivePower }} VAR
>{{ tagData?.reactivePower }} VAR
</div>
<div class="item">
<span class="card-title">功率因素: </span
>{{ tagData.powerFactor }}
>{{ tagData?.powerFactor }}
</div>
<div class="item">
<span class="card-title">频率: </span>{{ tagData.frequency }} Hz
<span class="card-title">频率: </span>{{ tagData?.frequency }} Hz
</div>
</div>
<div>
<div class="item">
<span class="card-title">正向有功: </span
>{{ tagData.forwardActiveEnergy }} kWh
>{{ tagData?.forwardActiveEnergy }} kWh
</div>
<div class="item">
<span class="card-title">正向无功: </span
>{{ tagData.forwardReactiveEnergy }} kvarh
>{{ tagData?.forwardReactiveEnergy }} kvarh
</div>
<div class="item">
<span class="card-title">反向有功: </span
>{{ tagData.reverseActiveEnergy }} kWh
>{{ tagData?.reverseActiveEnergy }} kWh
</div>
</div>
<div>
<div class="item">
<span class="card-title">反向无功: </span
>{{ tagData.reverseReactiveEnergy }} kvarh
>{{ tagData?.reverseReactiveEnergy }} kvarh
</div>
<div class="item">
<span class="card-title">视在功率: </span
>{{ tagData.apparentPower }} VA
>{{ tagData?.apparentPower }} VA
</div>
<div class="item">
<span class="card-title">谐波有功: </span
>{{ tagData.harmonicActivePower }} W
>{{ tagData?.harmonicActivePower }} W
</div>
<div class="item">
<span class="card-title">基波有功: </span
>{{ tagData.fundamentalActivePower }} W
>{{ tagData?.fundamentalActivePower }} W
</div>
<div class="item">
<span class="card-title">基波无功: </span
>{{ tagData.fundamentalReactivePower }} VAR
>{{ tagData?.fundamentalReactivePower }} VAR
</div>
</div>
</div>
@ -694,7 +714,8 @@ onMounted(async () => {
<el-col :span="12" :offset="0">联系人: {{ mapWorkCardData.owner }}</el-col>
<el-col :span="12" :offset="0">经纬度: {{ mapWorkCardData.jingwei }}</el-col>
<el-col :span="12" :offset="0"
>离线报警开关: {{ mapWorkCardData.alarm != 0 ? '开' : '关' }}</el-col
>离线报警开关: {{ mapWorkCardData.alarm != 0 ? '开' : '关' }}
</el-col
>
<el-col :span="12" :offset="0">服务商描述: {{ mapWorkCardData.deviceRemark }}</el-col>
<el-col :span="12" :offset="0">设备软件版本号: {{ mapWorkCardData.macVersion }}</el-col>
@ -708,6 +729,8 @@ onMounted(async () => {
<el-col :span="12" :offset="0">备注: {{ mapWorkCardData.remark }}</el-col>
</el-row>
</el-dialog>
</template>
<style lang="scss" scoped>
@ -718,26 +741,32 @@ onMounted(async () => {
line-height: 1.5;
}
}
.card-item {
margin-bottom: 15px;
text-align: left;
.item {
margin-bottom: 15px;
}
}
.card-title {
font-weight: bold;
color: #333;
}
:deep(.el-carousel__container) {
.el-carousel__item {
.el-image {
height: 100%;
img {
}
}
}
}
.home-index-banner {
background-color: #fff;
}
@ -798,6 +827,7 @@ onMounted(async () => {
cursor: pointer;
color: #333;
}
div:hover {
color: #fff;
background-color: #00377a;
@ -808,6 +838,7 @@ onMounted(async () => {
.item-container {
width: 100%;
text-align: center;
.item-nav {
// text-align: left;
// padding-left: 50px;
@ -865,6 +896,7 @@ onMounted(async () => {
font-size: 16px;
font-weight: normal;
color: #333;
&:hover {
color: #00377a;
border-bottom: 1px solid #00377a !important;
@ -882,6 +914,7 @@ onMounted(async () => {
}
}
}
.item-container:hover {
.sub-goods {
display: block !important;
@ -932,12 +965,15 @@ onMounted(async () => {
font-weight: bold;
letter-spacing: 1px;
}
.equip-pic {
display: flex;
flex-wrap: wrap;
li {
width: calc((100% - 100px) / 4);
margin: 8px 0 8px 20px;
&:nth-child(4n) {
margin-right: 20px;
}
@ -981,11 +1017,13 @@ onMounted(async () => {
width: calc(100% - 340px);
// padding-left: 35px;
overflow-y: auto;
.consult-box {
margin-bottom: 10px;
//height: 70px;
padding-left: 15px;
cursor: pointer;
.consult-title {
height: 30px;
display: flex;