iot对接

This commit is contained in:
jiang 2025-10-16 19:30:53 +08:00
parent 28f8908644
commit c689a77849
2 changed files with 336 additions and 206 deletions

View File

@ -1,15 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.jpg">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 贵州 -->
<title>智联装备云控平台</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=cClgLBaLgGUdQDilX9dGvieL"></script>
</body>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="text/javascript"
src="https://api.map.baidu.com/api?type=webgl&v=1.0&ak=XS1NJt7EsW7mVN48hMXDwMndxRJzSw30"></script>
<link href="//mapopen.bj.bcebos.com/github/BMapGLLib/DrawingManager/src/DrawingManager.min.css" rel="stylesheet">
<script src="//mapopen.bj.bcebos.com/github/BMapGLLib/DrawingManager/src/DrawingManager.min.js"></script>
</body>
</html>

View File

@ -1,23 +1,15 @@
<script setup lang="ts">
import EquipCard from 'components/equipCard.vue'
import EquipCardNew from 'components/equipCardNew/index.vue'
import NavMenu from 'components/Navmenu/index.vue'
import { getGoodsClassListApi, getCompanyListApi, getSwiperListApi } from 'http/api/home'
import { useStore } from 'store/user'
import { mainStore } from 'store/main'
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 { wgs84ToBd09Direct } from 'utils/map'
import { getMapData, mapLoginApi, getIotListApi, getBadgeInfoApi } from 'http/api/map'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getMapData } from 'http/api/map'
import icon1 from '@/assets/img/work-card.jpg'
import icon2 from '@/assets/img/IOT.jpg'
const router: any = useRouter()
const userStore = useStore()
const store: any = mainStore()
const tokenNew = localStorage.getItem('tokenNew')
const companyList: any = ref([])
const classList: any = ref([])
const selectOptions = ref<boolean>(false) //
@ -30,11 +22,21 @@ 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()
//
const map: any = ref(null)
//
const isDrawing = ref(false)
//
const geoFences = ref([])
//
const drawingManager = ref(null)
//
const showDrawingControls = ref(false)
// mapDialog
watch(
() => mapDialog.value,
@ -46,82 +48,29 @@ watch(
},
)
//
const loginApi = async () => {
try {
const res: any = await mapLoginApi({})
console.log('🚀 ~ loginApi ~ res:', res)
} catch (error) {
console.log('🚀 ~ loginApi ~ error:', error)
getMapDataList()
}
}
//
const getMapDataList = async () => {
try {
await getMapData()
} catch (res: any) {
console.log('--------------', res)
console.log('🚀 ~ getMapDataList ~ res:', res)
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,
lng: latitudeNum,
lat: longitudeNum,
item: item,
isWork: false,
}
})
points.value = [...JSON.parse(JSON.stringify(points.value)), ...point]
}
//getWorkData()
initMap()
}
}
//
const getWorkData = async () => {
try {
await getIotListApi()
} catch (res: any) {
console.log('🚀 ~ getWorkData ~ res:', res)
if (res.errorCode != 200) {
initMap()
return
}
const point = res.devices.map((item: any) => {
return {
id: item.user_id,
lng: item.jingdu,
lat: item.weidu,
isWork: true,
}
})
//
points.value = [...JSON.parse(JSON.stringify(points.value)), ...point]
console.log('🚀 ~ getWorkData ~ points.value:', points.value)
initMap()
}
}
//
const getWorkCardDetails = async (id: any) => {
try {
await getBadgeInfoApi(id)
} catch (res: any) {
console.log('🚀 ~ getWorkCardDetails ~ res:', res)
if (res.errorCode != 200) {
return
}
mapWorkCardData.value = res.data[0]
}
}
//
const getCompanyListData = async () => {
@ -157,29 +106,9 @@ const onSharedHallByCompany = (companyId: any, name: any) => {
})
}
const handlerGoodsDetails = (...arg: any) => {
const arr = arg.map((key: any, index: number) => {
return {
level: index + 1 + '',
typeId: key.id,
typeName: key.name,
}
})
router.push({
name: 'equipList',
state: { typeTag: arr },
})
}
const hotDeviceList: any = ref([])
/* 获取热搜装备 */
const getHotDeviceList = async () => {
const res: any = await getHotList({ pageSize: 3 })
hotDeviceList.value = res.data
}
/* 热搜卡片点击跳转至详情页 */
const onClick = (val: any) => {
router.push({
@ -213,16 +142,6 @@ const getSwiperListData = async () => {
// console.log(res, '')
}
const onClickSwiper = (item: any) => {
if (item.slideLink) {
const link = item.slideLink.startsWith('http')
? item.slideLink
: `https://${item.slideLink}`
window.open(link, '_blank')
} else {
router.push({ name: 'equipList' })
}
}
getSwiperListData()
@ -242,7 +161,6 @@ const formatUtcTimeV2 = (utcTime: any, date: any) => {
const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds))
// (UTC+8)
// const beijingOffset = 8 * 60 * 60 * 1000; // 8
const beijingTime = new Date(utcDate.getTime())
//
@ -259,7 +177,6 @@ const formatUtcTimeV2 = (utcTime: any, date: any) => {
.replace(/\//g, '-')} (北京时间)`
}
//
type PositionStatus = 'A' | 'V' | string
@ -275,7 +192,6 @@ type PositionSource = 'gnss' | 'lbs' | 'wifi' | string
const typeMap = {
'2': '小型数据终端',
'3': '智能工牌',
}
// type
@ -342,31 +258,213 @@ const getPositionSourceText = (source: PositionSource): string => {
return sourceMap[source] || (source || '-')
}
//
const initDrawingTool = (): void => {
if (!window.BMapGLLib) {
console.error('BMapLib 未加载,请确保已引入百度地图绘制库')
ElMessage.error('地图绘制功能加载失败,请刷新页面重试')
return
}
try {
//
drawingManager.value = new BMapGLLib.DrawingManager(map.value, {
isOpen: false,
drawingType: BMAP_DRAWING_MARKER,
enableDrawingTool: true,
enableCalculate: true,
drawingToolOptions: {
anchor: BMAP_ANCHOR_TOP_RIGHT,
offset: new BMapGL.Size(5, 5),
drawingModes: [
BMAP_DRAWING_CIRCLE,
BMAP_DRAWING_POLYGON,
BMAP_DRAWING_RECTANGLE,
],
},
...getDrawingStyleOptions(), //
})
//
drawingManager.value.addEventListener('overlaycomplete', handleDrawingComplete)
ElMessage.success('绘制工具初始化成功')
} catch (error) {
console.error('初始化绘制工具失败:', error)
ElMessage.error('绘制工具初始化失败')
}
}
//
const getDrawingStyleOptions = () => {
const commonStyle = {
strokeColor: '#FF0000',
fillColor: '#FF0000',
strokeWeight: 2,
strokeOpacity: 0.8,
fillOpacity: 0.2,
}
return {
circleOptions: commonStyle,
rectangleOptions: commonStyle,
polygonOptions: commonStyle,
}
}
//
const handleDrawingComplete = (e: any): void => {
console.log('绘制完成事件详情:', e)
//
showFenceDialog(e.overlay, e.drawingMode)
//
drawingManager.value.close()
}
//
const generateDefaultFenceName = (shapeType: string, fenceCount: number): string => {
const shapeNames: Record<string, string> = {
'polygon': '多边形围栏',
'rectangle': '矩形围栏',
'circle': '圆形围栏',
}
const baseName = shapeNames[shapeType] || '围栏'
return `${baseName}${fenceCount + 1}`
}
//
const createFenceData = (overlay: any, shapeType: string, fenceName: string): any => {
const baseData = {
id: Date.now(),
name: fenceName,
calculate: overlay.calculate,
shapeType: shapeType,
overlay: overlay,
}
switch (shapeType) {
case 'circle':
return {
...baseData,
center: overlay.getCenter(),
radius: overlay.getRadius(),
}
case 'polygon':
case 'rectangle':
return {
...baseData,
points: overlay.points,
}
default:
return baseData
}
}
//
const showFenceDialog = (overlay: any, shapeType: string): void => {
const defaultName = generateDefaultFenceName(shapeType, geoFences.value.length)
ElMessageBox.prompt('请输入地理围栏名称:', '创建地理围栏', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValue: defaultName,
inputPlaceholder: '请输入围栏名称',
inputValidator: validateFenceName,
})
.then(({ value }) => {
const fenceName = value.trim()
const fenceData = createFenceData(overlay, shapeType, fenceName)
//
bindOverlayEvents(overlay, fenceData)
saveFence(fenceData)
ElMessage.success(`围栏"${fenceName}"创建成功`)
})
.catch((error) => {
handleFenceCreationCancel(overlay, error)
})
}
//
const validateFenceName = (value: string): boolean | string => {
if (!value || value.trim() === '') {
return '围栏名称不能为空'
}
if (value.length > 20) {
return '围栏名称不能超过20个字符'
}
return true
}
//
const handleFenceCreationCancel = (overlay: any, error: any): void => {
console.log('用户取消创建围栏:', error)
//
if (overlay && map.value) {
map.value.removeOverlay(overlay)
}
//
if (error !== 'cancel') {
ElMessage.info('已取消创建围栏')
}
}
//
const bindOverlayEvents = (overlay: any, fence: any): void => {
overlay.addEventListener('click', (e: any) => {
showFenceInfo(fence)
})
}
//
const saveFence = (fenceData: any): void => {
//
geoFences.value.push(fenceData)
}
//
const showFenceInfo = (fence) => {
console.log(fence)
ElMessageBox.confirm(
`围栏名称: ${fence.name}`,
'地理围栏信息',
{
confirmButtonText: '删除围栏',
cancelButtonText: '查看详情',
type: 'info',
},
).then(() => {
map.value.removeOverlay(fence.overlay)
}).catch(() => {
})
}
const initMap = () => {
if (window.BMap) {
const map = new BMap.Map(mapContainer.value)
const point = new BMap.Point(106.674974, 26.489328) //
if (window.BMapGL) {
map.value = new BMapGL.Map(mapContainer.value)
const point = new BMapGL.Point(106.674974, 26.489328) //
//
points.value.forEach((item: any) => {
const point = new BMap.Point(item.lng, item.lat)
const point = new BMapGL.Point(item.lng, item.lat)
let icon = null
if (item.item.type == '3') {
icon = new BMap.Icon(icon1, new BMap.Size(30, 30), {
imageSize: new BMap.Size(30, 30),
icon = new BMapGL.Icon(icon1, new BMapGL.Size(30, 30), {
imageSize: new BMapGL.Size(30, 30),
})
} else {
icon = new BMap.Icon(icon2, new BMap.Size(30, 30), {
imageSize: new BMap.Size(30, 30),
icon = new BMapGL.Icon(icon2, new BMapGL.Size(30, 30), {
imageSize: new BMapGL.Size(30, 30),
})
}
const marker = icon ? new BMap.Marker(point, { icon }) : new BMap.Marker(point)
map.addOverlay(marker)
// const label = new BMap.Label(item.name, { offset: new BMap.Size(20, -10) })
// marker.setLabel(label) //
const marker = icon ? new BMapGL.Marker(point, { icon }) : new BMapGL.Marker(point)
map.value.addOverlay(marker)
marker.addEventListener('click', () => {
//
console.log('点击了标记点', item)
if (item.item.type == '3') {
//getWorkCardDetails(item.id)
mapWorkCard.value = true
@ -377,18 +475,22 @@ const initMap = () => {
mapData.value = item.item
tagData.value = mapData.value.aphase
tagIndex.value = 1
timer.value = setInterval(() => {
loginApi()
}, 180000)
}
})
})
map.addControl(new BMap.NavigationControl()) //
map.centerAndZoom(point, 11) //
map.enableScrollWheelZoom(true) //
// 7
//
initDrawingTool()
//map.value.addControl(new BMapGL.NavigationControl()) //
map.value.centerAndZoom(point, 11) //
map.value.enableScrollWheelZoom(true) //
ElMessage.success('地图初始化成功')
} else {
console.error('BMap 未加载')
ElMessage.error('地图加载失败,请检查网络连接')
}
}
@ -406,35 +508,7 @@ const handleTag = (tag: number) => {
}
onMounted(async () => {
loginApi()
if (!tokenNew) {
const origin = window.location.href
console.log(origin.split('ticket=')[1], 'origin')
if (origin.indexOf('ticket') != -1) {
// console.log('origin.splitticket')
const res: any = await loginNewApi({
ticket: origin.split('ticket=')[1].split('#/')[0],
sysType: 0,
})
store.setToken(res.data.access_token)
localStorage.setItem('tokenNew', res.data.access_token)
const result: any = await getUserInfoAPI()
store.setUserInfo(result.user)
const roles = result.roles
const isAdmin = roles.some((e: any) => e == 'admin')
if (isAdmin) {
localStorage.setItem('rolesType', '3')
} else {
localStorage.setItem('rolesType', '1')
}
userStore.editcurrentMenuItem('goodsManagement')
//
}
}
getMapDataList()
})
</script>
@ -572,7 +646,8 @@ onMounted(async () => {
<div class="iot-data-container">
<!-- 1.1 设备基本信息分区 -->
<div class="data-section">
<h3 class="section-heading">设备基础信息 <span class="item-value">{{ mapData.uuid || '-' }}</span></h3>
<h3 class="section-heading">设备基础信息 <span class="item-value">{{ mapData.uuid || '-' }}</span>
</h3>
<el-row :gutter="30" class="section-content">
<el-col :span="8">
<div class="data-item">
@ -836,49 +911,65 @@ onMounted(async () => {
<el-col :span="6">
<div class="data-item">
<span class="item-label">电压</span>
<span class="item-value">{{ tagData?.voltage ? `${Number(tagData.voltage).toFixed(2)} V` : '-' }}</span>
<span
class="item-value">{{ tagData?.voltage ? `${Number(tagData.voltage).toFixed(2)} V` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">电流</span>
<span class="item-value">{{ tagData?.current ? `${Number(tagData.current).toFixed(2)} A` : '-' }}</span>
<span
class="item-value">{{ tagData?.current ? `${Number(tagData.current).toFixed(2)} A` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">有功功率</span>
<span class="item-value">{{ tagData?.activePower ? `${Number(tagData.activePower).toFixed(2)} W` : '-' }}</span>
<span
class="item-value">{{ tagData?.activePower ? `${Number(tagData.activePower).toFixed(2)} W` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">无功功率</span>
<span class="item-value">{{ tagData?.reactivePower ? `${Number(tagData.reactivePower).toFixed(2)} VAR` : '-' }}</span>
<span
class="item-value">{{ tagData?.reactivePower ? `${Number(tagData.reactivePower).toFixed(2)} VAR` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">功率因素</span>
<span class="item-value">{{ tagData?.powerFactor ? `${Number(tagData.powerFactor).toFixed(2)}` : '-' }}</span>
<span
class="item-value">{{ tagData?.powerFactor ? `${Number(tagData.powerFactor).toFixed(2)}` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">频率</span>
<span class="item-value">{{ tagData?.frequency ? `${Number(tagData.frequency).toFixed(2)} Hz` : '-' }}</span>
<span
class="item-value">{{ tagData?.frequency ? `${Number(tagData.frequency).toFixed(2)} Hz` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">正向有功</span>
<span class="item-value">{{ tagData?.forwardActiveEnergy ? `${Number(tagData.forwardActiveEnergy).toFixed(2)} kWh` : '-' }}</span>
<span
class="item-value">{{ tagData?.forwardActiveEnergy ? `${Number(tagData.forwardActiveEnergy).toFixed(2)} kWh` : '-'
}}</span>
</div>
</el-col>
<el-col :span="6">
<div class="data-item">
<span class="item-label">正向无功</span>
<span class="item-value">{{ tagData?.forwardReactiveEnergy ? `${Number(tagData.forwardReactiveEnergy).toFixed(2)} kvarh` : '-' }}</span>
<span
class="item-value">{{ tagData?.forwardReactiveEnergy ? `${Number(tagData.forwardReactiveEnergy).toFixed(2)} kvarh` : '-'
}}</span>
</div>
</el-col>
</el-row>
@ -892,44 +983,6 @@ onMounted(async () => {
</template>
</el-dialog>
<!-- &lt;!&ndash; 工牌信息弹框 &ndash;&gt;-->
<!-- <el-dialog v-model="mapWorkCard" title="工牌信息" width="50%">-->
<!-- <el-row :gutter="20" class="work-card">-->
<!-- <el-col :span="12" :offset="0">设备名称: {{ mapWorkCardData.user_name }}</el-col>-->
<!-- <el-col :span="12" :offset="0">最近定位时间: {{ mapWorkCardData.sys_time }}</el-col>-->
<!-- <el-col :span="12" :offset="0">最近位置更新时间: {{ mapWorkCardData.datetime }}</el-col>-->
<!-- <el-col :span="12" :offset="0">最近信号时间: {{ mapWorkCardData.heart_time }}</el-col>-->
<!-- <el-col :span="12" :offset="0">超速设置值: {{ mapWorkCardData.sudu }}</el-col>-->
<!-- <el-col :span="12" :offset="0">状态: {{ mapWorkCardData.status }}</el-col>-->
<!-- <el-col :span="12" :offset="0">激活时间: {{ mapWorkCardData.use_time }}</el-col>-->
<!-- <el-col :span="12" :offset="0">电话: {{ mapWorkCardData.tel }}</el-col>-->
<!-- <el-col :span="12" :offset="0">车牌号: {{ mapWorkCardData.sex }}</el-col>-->
<!-- <el-col :span="12" :offset="0">设备号: {{ mapWorkCardData.sim_id }}</el-col>-->
<!-- <el-col :span="12" :offset="0">手机号码: {{ mapWorkCardData.phone }}</el-col>-->
<!-- <el-col :span="12" :offset="0">设备ID: {{ mapWorkCardData.user_id }}</el-col>-->
<!-- <el-col :span="12" :offset="0">型号: {{ mapWorkCardData.product_type }}</el-col>-->
<!-- <el-col :span="12" :offset="0">图标序号: {{ mapWorkCardData.iconType }}</el-col>-->
<!-- <el-col :span="12" :offset="0">用户到期时间: {{ mapWorkCardData.out_time }}</el-col>-->
<!-- <el-col :span="12" :offset="0">经度: {{ mapWorkCardData.jingdu }}</el-col>-->
<!-- <el-col :span="12" :offset="0">iccid: {{ mapWorkCardData.su }}</el-col>-->
<!-- <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-->
<!-- >-->
<!-- <el-col :span="12" :offset="0">服务商描述: {{ mapWorkCardData.deviceRemark }}</el-col>-->
<!-- <el-col :span="12" :offset="0">设备软件版本号: {{ mapWorkCardData.macVersion }}</el-col>-->
<!-- <el-col :span="12" :offset="0">视频地址: {{ mapWorkCardData.VideoConfig }}</el-col>-->
<!-- <el-col :span="12" :offset="0">高温报警值: {{ mapWorkCardData.HighTempAlarm }}</el-col>-->
<!-- <el-col :span="12" :offset="0">低温报警值: {{ mapWorkCardData.LowTempAlarm }}</el-col>-->
<!-- <el-col :span="12" :offset="0">厂商编码: {{ mapWorkCardData.VendorCode }}</el-col>-->
<!-- <el-col :span="12" :offset="0">业主ID: {{ mapWorkCardData.OwnerId }}</el-col>-->
<!-- <el-col :span="12" :offset="0">终端类型: {{ mapWorkCardData.TerminalType }}</el-col>-->
<!-- <el-col :span="12" :offset="0">设备编码: {{ mapWorkCardData.DeviceCode }}</el-col>-->
<!-- <el-col :span="12" :offset="0">备注: {{ mapWorkCardData.remark }}</el-col>-->
<!-- </el-row>-->
<!-- </el-dialog>-->
<el-dialog
v-model="mapWorkCard"
title="工牌"
@ -942,7 +995,8 @@ onMounted(async () => {
<div class="iot-data-container">
<!-- 1. 设备基础信息区 -->
<div class="data-section">
<h3 class="section-heading">设备基础信息<span class="item-value">{{ mapWorkCardData.uuid || '-' }}</span></h3>
<h3 class="section-heading">设备基础信息<span class="item-value">{{ mapWorkCardData.uuid || '-'
}}</span></h3>
<el-row :gutter="30" class="section-content">
<el-col :span="8">
<div class="data-item">
@ -1119,7 +1173,6 @@ onMounted(async () => {
</div>
</div>
</el-dialog>
</template>
<style lang="scss" scoped>
@ -1324,6 +1377,78 @@ onMounted(async () => {
margin-top: 20px;
flex: 1;
border-radius: 8px;
position: relative;
.drawing-controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 6px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
display: flex;
gap: 8px;
flex-wrap: wrap;
max-width: 300px;
}
.fence-list {
position: absolute;
top: 70px;
left: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
padding: 10px;
border-radius: 6px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
max-width: 250px;
max-height: 300px;
overflow-y: auto;
h4 {
margin: 0 0 10px 0;
font-size: 14px;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.fence-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
margin-bottom: 5px;
background: #f8f9fa;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background: #e9ecef;
}
.fence-name {
font-size: 12px;
font-weight: 500;
color: #333;
}
.fence-points {
font-size: 11px;
color: #666;
background: #dee2e6;
padding: 2px 6px;
border-radius: 10px;
}
}
.fence-item:last-child {
margin-bottom: 0;
}
}
}
}
}
@ -1527,4 +1652,4 @@ onMounted(async () => {
background-color: #f1f1f1;
border-radius: 4px;
}
</style>
</style>