chuangte_bike_newxcx/page_shanghu/admin_index.vue
2025-08-30 17:38:15 +08:00

1232 lines
37 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<u-navbar title="管理" :is-back='false' :border-bottom="false" :background="bgc" title-color='#2E4975'
title-size='36' height='45'></u-navbar>
<view @touchmove.stop.prevent="() => {}">
<view class="slider">
<view class="tit">
电量区间
</view>
<view style="width: 650rpx;margin: 0 auto;">
<slider-range :value="rangeValue" :min="rangeMin" :max="rangMax" :step="1" :bar-height="3"
:block-size="20" background-color="#EEEEF6" active-color="#4C97E7" :format="format"
:decorationVisible="true" @change="handleRangeChange"
@regionchange="onMapRegionChange"></slider-range>
</view>
</view>
</view>
<map class="map" id="map" ref="map" :scale="zoomSize" :latitude="latitude" :longitude="longitude" :circles="circles" :show-location="true" :markers="markers" :polygons="polyline" @markertap="onMarkerTap" @regionchange="onMapRegionChange">
<view class="center-marker">
<image src="https://lxnapi.ccttiot.com/bike/img/static/uZdgdHaduvMTKOpMKYxZ" style="width: 42rpx; height: 78rpx;" v-if="showdevList" />
</view>
</map>
<view class="park" @click="toggleIconAndCallout">
<image src="https://api.ccttiot.com/smartmeter/img/static/uQnwY6GFEg9jCgIR7GEz" mode=""></image>
</view>
<view class="park" @click="showdevList=!showdevList" style="bottom: 600rpx;">
<image src="https://api.ccttiot.com/smartmeter/img/static/uP0Jd1XAgfjBj4NM9a8g" mode=""></image>
</view>
<view class="park" @click="btnshuaixn" style="bottom: 700rpx;width: 82rpx;height: 82rpx;border-radius: 50%;background-color: #fff;text-align: center;line-height: 82rpx;">
刷新
</view>
<view @touchmove.stop.prevent="() => {}">
<view class="btn_box">
<view class="btn1" @click="qecodelock()">
车辆扫码
</view>
<view class="btn2" @click="topage()">
车辆搜索
</view>
</view>
<view class="decice_cont">
<view class="cont" @click="changeidx('0')">
<view class="text" :class="statusidx=='0'?'act1':''">
仓库中
</view>
<view class="text" :class="statusidx=='-0'?'act1':''">
{{qbobj.device.statusCount[0] == null ? '0' : qbobj.device.statusCount[0]}}
</view>
</view>
<view class="cont" @click="changeidx('1')">
<view class="text" :class="statusidx==1?'act1':''">
待租
</view>
<view class="text" :class="statusidx==1?'act1':''">
{{qbobj.device.statusCount[1] == null ? '0' : qbobj.device.statusCount[1]}}
</view>
</view>
<view class="cont" @click="changeidx('3')">
<view class="text" :class="statusidx==3?'act1':''">
骑行中
</view>
<view class="text" :class="statusidx==3?'act1':''">
{{qbobj.device.statusCount[3] == null ? '0' : qbobj.device.statusCount[3]}}
</view>
</view>
<view class="cont" @click="changeidx('4')">
<view class="text" :class="statusidx==4?'act1':''">
临时锁车
</view>
<view class="text" :class="statusidx==4?'act1':''">
{{qbobj.device.statusCount[4] == null ? '0' : qbobj.device.statusCount[4]}}
</view>
</view>
<view class="cont" @click="changeidx('8')">
<view class="text" :class="statusidx==8?'act1':''">
禁用中
</view>
<view class="text" :class="statusidx==8?'act1':''">
{{qbobj.device.statusCount[8] == null ? '0' : qbobj.device.statusCount[8]}}
</view>
</view>
<view class="cont" @click="changeidx('6')">
<view class="text" :class="statusidx==6?'act1':''">
调度中
</view>
<view class="text" :class="statusidx==6?'act1':''">
{{qbobj.device.statusCount[6] == null ? '0' : qbobj.device.statusCount[6]}}
</view>
</view>
<view class="cont" @click="changeidx('20')">
<view class="text" :class="statusidx==20?'act1':''">
离线
</view>
<view class="text" :class="statusidx==20?'act1':''">
{{qbobj.device.onlineStatusCount[0] == null ? '0' : qbobj.device.onlineStatusCount[0]}}
</view>
</view>
<view class="cont" @click="changeidx('10')">
<view class="text" :class="statusidx==10?'act1':''">
在线
</view>
<view class="text" :class="statusidx==10?'act1':''">
{{qbobj.device.onlineStatusCount[1] == null ? '0' : qbobj.device.onlineStatusCount[1]}}
</view>
</view>
<view class="cont" @click="changeidx('9')">
<view class="text" :class="statusidx==9?'act1':''">
强制锁车
</view>
<view class="text" :class="statusidx==9?'act1':''">
{{qbobj.device.statusCount[9] == null ? '0' : qbobj.device.statusCount[9]}}
</view>
</view>
<view class="cont" @click="changeidx('7')">
<view class="text" :class="statusidx==7?'act1':''">
全部设备
</view>
<view class="text" :class="statusidx==7?'act1':''">
{{qbobj.device.count == null ? '--' : qbobj.device.count}}
</view>
</view>
</view>
</view>
<view class="fixdivce" v-if="nearbyMarkers.length>1&&showdevList">
<view class="scrollable-content">
<view class="divce_li" v-for="(item,index) in nearbyMarkers" :key="index" @click="tapsn(item.id)">
<view class="right_cont">
<view class="right_top">
<view class="right_top_right">
牌:{{item.vehicleNum == null ? '--' : item.vehicleNum}}
</view>
<view class="right_top_left">
<image src=" https://lxnapi.ccttiot.com/bike/img/static/u9pggdTCxcZgUTNsEvXQ" mode=""
v-if="item.remainingPower<=39"></image>
<image src="https://lxnapi.ccttiot.com/bike/img/static/uu1004113wsUShxo11X9" mode=""
v-if="item.remainingPower>=40&&item.remainingPower<=69"></image>
<image src="https://lxnapi.ccttiot.com/bike/img/static/uRI1LMpzqfIM060BO7np" mode=""
v-if="item.remainingPower>=70&&item.remainingPower<=100"></image>
{{item.remainingPower}}%
</view>
</view>
<view class="right_bot">
SN:{{item.sn}}
<span>{{status(item)}}</span>
</view>
</view>
</view>
</view>
</view>
<tab-bar :indexs='2' style=""></tab-bar>
</view>
</template>
<script>
import TabBar from '@/page_shanghu/components/tab-bar/tab-bar.vue';
import SliderRange from '@/page_shanghu/components/primewind-sliderrange/index.vue'
let pollIntervalId = null; // 轮询interval
let rangeDebounceId = null; // 滑动筛选debounce
var appMap = null;
export default {
data() {
return {
bgc: {
backgroundColor: "#F7FAFE",
},
latitude: 0,
longitude: 0,
isMap: false,
zoomSize: 15,
markers: [],
polyline: [],
polygons: [],
fixdata: [],
eledata: [],
listData: [],
allDeviceData: [], // 新增:存储所有设备数据
infonum: {},
rangeMin: 0,
rangMax: 100,
rangeValue: [0, 100],
status0: [], //未上架
status1: [], //正常
status2: [], //预约中
status3: [], //骑行中
status4: [], //临时锁车
status8: [], //下线
status9: [], //离线
markdata: {},
statusidx: '7',
deviceNum: {},
areaId: 0,
showIconAndCallout: false,
nearbyMarkers: [],
showdevList: false,
circles: [], // 用于存储圆形区域的数据
qbobj:{},
sn:''
}
},
components: {
SliderRange,TabBar
},
onShow() {
console.log('admin_index onShow -> start poll')
this.btnshuaixn()
if (pollIntervalId) {
clearInterval(pollIntervalId)
}
pollIntervalId = setInterval(() => {
console.log('admin_index poll tick')
this.btnshuaixn()
}, 10000)
},
onLoad() {
uni.getLocation({
type: 'gcj02',
success: function(lb) {
console.log(this.areaInfo, 'that.areaInfo')
},
fail: function(error) {
uni.showToast({
title: '未获取到定位信息,请点击设置勾选允许位置信息,即可使用全部功能',
icon: 'none',
duration: 2000
})
}
})
this.polyline = []
if (uni.getStorageSync('adminAreaid')) {
this.areaId = uni.getStorageSync('adminAreaid')
this.getArea()
console.log(this.areaId)
}
// this.getqb()
// this.getmarks()
},
onHide() {
if (pollIntervalId) {
clearInterval(pollIntervalId)
pollIntervalId = null
}
},
onUnload() {
if (pollIntervalId) {
clearInterval(pollIntervalId)
pollIntervalId = null
}
},
computed: {
userId() {
return this.$store.getters.userId;
},
},
methods: {
// 基于 ID 合并 markers避免清空造成闪烁
mergeMarkers(newMarkers) {
try {
const newIdSet = new Set(newMarkers.map(m => String(m.id)))
const preserved = this.markers.filter(m => m && m.isParking)
const merged = [...preserved]
// 使用新数据覆盖或新增
newMarkers.forEach(m => {
merged.push(m)
})
this.$set(this, 'markers', merged)
} catch (e) {
console.error('mergeMarkers error', e)
}
},
btnshuaixn(){
this.getqb()
this.getmarks()
},
// 请求全部运营统计数据
getqb() {
let StatKeys = 'device_status_count,device_online_status_count,device_count'
this.$u.get(`/dashboard/stat?keys=${StatKeys}&areaId=${this.areaId}`).then(res => {
if (res.code== 200) {
this.qbobj = res.data
}
})
},
status(item) {
if (item.status == 0) {
return '仓库中'
} else if (item.status == 1) {
return '待租'
} else if (item.status == 2) {
return '预约中'
} else if (item.status == 3) {
return '骑行中'
} else if (item.status == 4) {
return '临时锁车中'
} else if (item.status == 6) {
return '调度中'
} else if (item.status == 7) {
return '未绑定'
} else if (item.status == 8) {
return '禁用中'
}
},
onMapRegionChange(event) {
if (event.detail.type == 'end') {
this.getCenterLocation()
}
// 你可以在这里执行你需要的操作
},
getCenterLanLat() {
let that = this
uni.createMapContext("map", this).getCenterLocation({
type: 'gcj02',
success: (res) => {
},
fail: (err) => {}
})
},
getCenterLocation() {
const mapContext = uni.createMapContext('map')
mapContext.getCenterLocation({
success: (res) => {
console.log('中心点的经纬度:', res.longitude, res.latitude)
this.getNearbyMarkers(res.latitude, res.longitude)
},
fail: (err) => {
console.error('获取中心点定位失败:', err)
}
})
},
getCenter: function() {
var _that = this
appMap.getCurrentCenter(
function(state, point) {
if (0 == state) {
// 反编码
var point = new plus.maps.Point(point["longitude"], point["latitude"]);
plus.maps.Map.reverseGeocode(point, {}, function(event) {
var address = event.address // 转换后的地理位置
var coord = event.coord // 转换后的坐标信息
var coordType = event.coordType // 转换后的坐标系类型
console.log("Address:" + address)
console.log("coord", coord)
uni.showModal({
title: "提示",
content: "确定:" + address + "?",
success: function(res) {
if (res.confirm) {
// 业务逻辑...
} else if (res.cancel) {}
}
})
}, function(e) {
uni.showToast({
title: '反编码失败' + JSON.stringify(e)
})
})
} else {
uni.showToast({
icon: "none",
title: "获取经纬度失败!" + state
})
}
}
)
},
getNearbyMarkers(clickedLat, clickedLon) {
// 只有在 showdevList 为 true 的时候才显示圆形区域
if (this.showdevList) {
const nearbyMarkers = this.listData.filter(item => {
if (item.latitude && item.longitude) {
const distance = this.haversineDistance(
parseFloat(clickedLat),
parseFloat(clickedLon),
parseFloat(item.latitude),
parseFloat(item.longitude)
)
return distance <= 100
}
return false
})
// 根据 remainingPower 进行升序排序
nearbyMarkers.sort((a, b) => {
return a.remainingPower - b.remainingPower
})
// 更新 circles 数组来绘制淡灰色的圆形区域
this.circles = [{
latitude: clickedLat,
longitude: clickedLon,
color: '#fff', // 圆形边框颜色(淡灰色)
fillColor: '#00000010', // 圆形填充颜色(淡灰色)
radius: 100, // 圆的半径(单位:米)
strokeWidth: 1
}]
this.nearbyMarkers = nearbyMarkers
} else {
// 当 showdevList 为 false 时,清空 circles 数组
this.circles = []
}
},
haversineDistance(lat1, lon1, lat2, lon2) {
const R = 6371e3 // Earth radius in meters
const toRad = angle => angle * Math.PI / 180
const dLat = toRad(lat2 - lat1)
const dLon = toRad(lon2 - lon1)
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
},
async setMapScale(e, val) {
let mapContext = uni.createMapContext('map', this)
let setScale = () => {
return new Promise((resolve, reject) => {
mapContext.getScale({
success: r => {
resolve()
}
})
})
}
await setScale()
mapContext.moveToLocation({
success: (res) => {
const timer = setTimeout(() => {
clearTimeout(timer)
}, 500)
},
})
},
tapsn(sn) {
uni.navigateTo({
url: '/page_shanghu/guanli/device_detail?id=' + sn
})
},
qecodelock() {
uni.scanCode({
onlyFromCamera: true,
scanType: ['qrCode'],
success: res => {
console.log(res)
function getQueryParam(url, paramName) {
let regex = new RegExp(`[?&]${paramName}=([^&]*)`)
let results = regex.exec(url)
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null
}
let sceneValue = res.result
let decodedValue = decodeURIComponent(sceneValue)
this.sn = getQueryParam(decodedValue, 's')
console.log(this.sn,'....')
if (this.sn != '') {
this.$u.get('/bst/device?sn=' + this.sn).then((res) => {
if (res.code === 200) {
console.log(res, 'rererer')
if (res.data.areaId == this.areaId) {
uni.navigateTo({
url: '/page_shanghu/guanli/device_detail?id=' + res.data.id
})
} else {
uni.showToast({
title: '无效车辆',
icon: 'none',
duration: 2000
})
}
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
}
},
fail: err => {
console.error('扫描失败:', err)
uni.showToast({
title: '扫描失败',
icon: 'none'
})
}
})
},
topage() {
uni.navigateTo({
url: '/page_shanghu/gongzuotai/woke_deviceMgmt'
})
},
changeidx(ids) {
this.statusidx = ids
this.getmarks()
},
format(val) {
return val + '%'
},
handleRangeChange(e) {
console.log(e,'121212');
// 每次滑动时清除之前的定时器
if (rangeDebounceId) {
clearTimeout(rangeDebounceId)
}
// 设置一个新的定时器,在滑动停止后 500ms 执行筛选
rangeDebounceId = setTimeout(() => {
this.rangeValue = e
// 使用本地数据进行筛选
this.filterDevicesByPowerRange()
}, 500)
},
// 新增:根据电量范围筛选设备
filterDevicesByPowerRange() {
const [minPower, maxPower] = this.rangeValue
this.listData = this.allDeviceData.filter(device => {
const power = device.remainingPower
return power >= minPower && power <= maxPower
})
// 重置状态数组
this.status0 = []
this.status1 = []
this.status2 = []
this.status3 = []
this.status4 = []
this.status8 = []
this.status9 = []
this.status6 = []
// 重新分类设备
this.listData.forEach(item => {
if (item.status == 0) {
this.status0.push(item)
} else if (item.status == 1) {
this.status1.push(item)
} else if (item.status == 2) {
this.status2.push(item)
} else if (item.status == 3) {
this.status3.push(item)
} else if (item.status == 4) {
this.status4.push(item)
} else if (item.status == 6) {
this.status6.push(item)
} else if (item.status == 8) {
this.status8.push(item)
} else if (item.status == 9) {
this.status9.push(item)
}
})
// 更新地图标记(增量合并,避免闪烁)
const newMarkers = this.markstause()
this.mergeMarkers(newMarkers)
},
onMarkerTap(e) {
if (e.type === 'markertap') {
console.log('点击了标记:',this.listData, e.markerId)
// 这里可以根据需要处理点击标记的逻辑
// 阻止事件冒泡
this.sn = e.markerId
for (let i = 0; i < this.listData.length; i++) {
if (this.listData[i].id == this.sn) {
uni.navigateTo({
url: '/page_shanghu/guanli/device_detail?id=' + this.sn
})
}else if(this.listData[i].sn == this.sn){
uni.navigateTo({
url: '/page_shanghu/guanli/device_detail?id=' + this.listData[i].id
})
}
}
}
},
getArea() {
let id = this.areaId
this.$u.get(`/bst/area/${id}`).then((res) => { //查询停车列表
if (res.code == 200) {
this.latitude = res.data.latitude
this.longitude = res.data.longitude
const polylines = this.convertBoundaryToPolyline(res.data.boundaryStr)
this.polyline.push(polylines)
setTimeout(() => {
this.getParking()
}, 500)
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
},
convertBoundaryToPolylines(boundaries, num) {
const colorMap = {
1: {
fillColor: "#3A7EDB10",
strokeColor: "#3A7EDB"
},
2: {
fillColor: "#FFF5D640",
strokeColor: "#FF473E"
},
3: {
fillColor: "#FFD1CF40",
strokeColor: "#FFC107"
}
}
if (!colorMap[num]) {
console.error("Invalid type number:", num)
return []
}
return boundaries.map(boundary => {
if (!boundary) return null
let coords
try {
coords = JSON.parse(boundary)
} catch (error) {
console.error("Error parsing boundary JSON:", error)
return null
}
if (!Array.isArray(coords)) {
console.error("Parsed boundary is not an array:", coords)
return null
}
const points = coords.map(coord => ({
latitude: parseFloat(coord[1]),
longitude: parseFloat(coord[0])
}))
return {
points: points,
fillColor: colorMap[num].fillColor,
strokeColor: colorMap[num].strokeColor,
strokeWidth: 2, //描边宽度
zIndex: 1, //层级
}
}).filter(polyline => polyline !== null) // 过滤掉无效的折线数据
},
markstause() {
const deviceMarkers = []
this.status0.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
iconPath: item.onlineStatus == 0 ?
'https://lxnapi.ccttiot.com/bike/img/static/uQRng4QNKA38Amk8Wgt5' :
'https://lxnapi.ccttiot.com/bike/img/static/uocjFo8Ar2BJVpzC2G2f',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#ffffff', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#000000', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
// https://lxnapi.ccttiot.com/bike/img/static/u1UD93BU1vfshWFoDwgX
// https://lxnapi.ccttiot.com/bike/img/static/u4UKmB47AxOj3YKIaajM
this.status1.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
// joinCluster: true,
height: 34,
iconPath: item.onlineStatus == 0 ?
'https://lxnapi.ccttiot.com/bike/img/static/uzhMeExOQJbMcZtrfGUV' :
'https://lxnapi.ccttiot.com/bike/img/static/uheL17wVZn24BwCwEztT',
// iconPath: 'https://lxnapi.ccttiot.com/bike/img/static/uAajBTcihvOr9HttyWck',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#2679D1', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#D4ECFF', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
this.status2.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
iconPath: item.onlineStatus == 0 ?
'https://lxnapi.ccttiot.com/bike/img/static/uR3DQEssiK62ovhh88y8' :
'https://lxnapi.ccttiot.com/bike/img/static/u460R1NKWHEpHbt0U4H7',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#2679D1', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#D4ECFF', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
this.status3.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
// joinCluster: true,
iconPath: item.onlineStatus == 0 ? 'https://lxnapi.ccttiot.com/bike/img/static/uG13E7BpUFF44wVYC9no' : 'https://lxnapi.ccttiot.com/bike/img/static/uHQIdWCTmtUztl49wBKU',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#2679D1', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#D4ECFF', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
this.status4.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
// joinCluster: true,
iconPath: item.onlineStatus == 0 ? 'https://lxnapi.ccttiot.com/bike/img/static/uRod2zf3t9dAOYafWoWt' : 'https://lxnapi.ccttiot.com/bike/img/static/uZpXq3TBtM5gVgJJeImY',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#2679D1', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#D4ECFF', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
this.status6.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
// joinCluster: true,
iconPath: item.onlineStatus == 0 ?
'https://lxnapi.ccttiot.com/bike/img/static/uhZudZM3nEKj0tYKlho2' :
'https://lxnapi.ccttiot.com/bike/img/static/ujur6TezvPf4buFAqPHo',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#2679D1', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#D4ECFF', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
this.status8.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
iconPath: item.onlineStatus == 0 ?
'https://lxnapi.ccttiot.com/bike/img/static/ucBKG3ebYRAToVweJihu' :
'https://lxnapi.ccttiot.com/bike/img/static/uyK7Vg4Lu8xb3oNVuG2l',
callout: {
content: '' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#ffffff', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#000000', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
this.status9.forEach(item => {
deviceMarkers.push({
id: parseFloat(item.sn),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
// title: item.deviceName,
width: 30,
height: 34,
iconPath: item.onlineStatus == 0 ?
'https://api.ccttiot.com/smartmeter/img/static/uQPc1AJSqJ6ZbOWrrLrf' :
'https://api.ccttiot.com/smartmeter/img/static/u392YZI3qzhhi3pORotr',
callout: {
content:'' + parseInt(item.remainingPower) + '%', // 修改为你想要显示的文字内容
color: '#ffffff', // 修改为文字颜色
fontSize: 10, // 修改为文字大小
borderRadius: 10, // 修改为气泡圆角大小
bgColor: '#000000', // 修改为气泡背景颜色
padding: 3, // 修改为气泡内边距
display: 'ALWAYS', // 修改为气泡的显示策略
}
})
})
return deviceMarkers
},
getmarks() {
this.status0 = []
this.status1 = []
this.status2 = []
this.status3 = []
this.status4 = []
this.status8 = []
this.status9 = []
this.status6 = []
// 如果已经有数据,直接使用本地筛选
// if (this.allDeviceData.length > 0) {
// this.filterDevicesByPowerRange()
// return
// }
if (this.statusidx != 7 && this.statusidx != 20 && this.statusidx != 10) {
this.$u.get(`/bst/device/all?areaId=${this.areaId}&status=${this.statusidx}&powerRange=${this.rangeValue[0] + ',' + this.rangeValue[1]}&refresh=true`).then((res) => {
if (res.code == 200) {
this.showmap = true
this.allDeviceData = res.data // 保存所有数据
this.listData = res.data
res.data.forEach(item => {
if (item.status == 0) {
this.status0.push(item)
} else if (item.status == 1) {
this.status1.push(item)
} else if (item.status == 2) {
this.status2.push(item)
} else if (item.status == 3) {
this.status3.push(item)
} else if (item.status == 4) {
this.status4.push(item)
} else if (item.status == 6) {
this.status6.push(item)
} else if (item.status == 8) {
this.status8.push(item)
}else if (item.status == 9) {
this.status9.push(item)
}
})
const newMarkers = this.markstause()
this.mergeMarkers(newMarkers)
}
})
} else if (this.statusidx == 7) {
this.$u.get(`/bst/device/all?areaId=${this.areaId}&powerRange=${this.rangeValue[0] + ',' + this.rangeValue[1]}&refresh=true`).then((res) => {
if (res.code == 200) {
this.showmap = true
this.allDeviceData = res.data // 保存所有数据
this.listData = res.data
res.data.forEach(item => {
if (item.status == 0) {
this.status0.push(item)
} else if (item.status == 1) {
this.status1.push(item)
} else if (item.status == 2) {
this.status2.push(item)
} else if (item.status == 3) {
this.status3.push(item)
} else if (item.status == 4) {
this.status4.push(item)
} else if (item.status == 6) {
this.status6.push(item)
} else if (item.status == 8) {
this.status8.push(item)
}else if (item.status == 9) {
this.status9.push(item)
}
})
const newMarkers = this.markstause()
this.mergeMarkers(newMarkers)
}
})
} else if (this.statusidx == 20 || this.statusidx == 10) {
this.$u.get(`/bst/device/all?areaId=${this.areaId}&onlineStatus=${this.statusidx == 20 ? 0 : 1}&powerRange=${this.rangeValue[0] + ',' + this.rangeValue[1]}&refresh=true`).then((res) => {
if (res.code == 200) {
this.showmap = true
this.allDeviceData = res.data // 保存所有数据
this.listData = res.data
res.data.forEach(item => {
if (item.status == 0) {
this.status0.push(item)
} else if (item.status == 1) {
this.status1.push(item)
} else if (item.status == 2) {
this.status2.push(item)
} else if (item.status == 3) {
this.status3.push(item)
} else if (item.status == 4) {
this.status4.push(item)
} else if (item.status == 6) {
this.status6.push(item)
} else if (item.status == 8) {
this.status8.push(item)
}else if (item.status == 9) {
this.status9.push(item)
}
})
const newMarkers = this.markstause()
this.mergeMarkers(newMarkers)
}
})
}
this.clusterMarkers() // 调用聚合方法
},
clusterMarkers() {
this.markers.forEach(marker => {
let added = false
for (let i = 0; i < this.clusters.length; i++) {
const cluster = this.clusters[i]
const distance = this.calculateDistance(cluster.latitude, cluster.longitude, marker.latitude, marker.longitude)
if (distance < this.clusterRadius) {
// 合并到现有聚合
cluster.markers.push(marker)
cluster.latitude = (cluster.latitude * cluster.markers.length + marker.latitude) / (cluster.markers.length + 1)
cluster.longitude = (cluster.longitude * cluster.markers.length + marker.longitude) / (cluster.markers.length + 1)
added = true
break
}
}
if (!added) {
// 创建新的聚合
this.clusters.push({
latitude: marker.latitude,
longitude: marker.longitude,
markers: [marker]
})
}
})
},
// 计算两点间的距离
calculateDistance(lat1, lon1, lat2, lon2) {
const p = 0.017453292519943295 // Math.PI / 180
const c = Math.cos;
const a = 0.5 - c((lat2 - lat1) * p) / 2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p)) / 2
return 12742 * Math.asin(Math.sqrt(a)) // 2 * R; R = 6371 km
},
toggleIconAndCallout() {
this.showIconAndCallout = !this.showIconAndCallout
this.markers.forEach(marker => {
if (marker.isParking) {
marker.callout.display = this.showIconAndCallout ? 'ALWAYS' : 'BYCLICK'
}
})
},
getParking() {
let data = {
areaId: this.areaId
}
this.$u.get(`/bst/areaSub/listByAreaId?areaId=${this.areaId}`).then((res) => {
if (res.code === 200) {
const newMarkers = []
const type1Data = []
const type2Data = []
const type3Data = []
res.data.forEach(row => {
if(row.status == 0){
if (row.type == 1) {
type1Data.push(row)
} else if (row.type == 2) {
type2Data.push(row)
} else if (row.type == 3) {
type3Data.push(row)
}
console.log(row,'121212')
newMarkers.push({
id: row.id,
latitude: row.latitude,
longitude: row.longitude,
width: 20,
height: 29,
iconPath: row.type == 1 ? 'https://lxnapi.ccttiot.com/bike/img/static/up2xXqAgwCX5iER600k3' : row.type == 2 ? 'https://lxnapi.ccttiot.com/bike/img/static/uDNY5Q4zOiZTCBTA2Jdq' : 'https://lxnapi.ccttiot.com/bike/img/static/u53BAQcFIX3vxsCzEZ7t',
callout: {
content: row.name,
color: '#ffffff',
fontSize: 14,
borderRadius: 10,
bgColor: row.type == 1 ? '#3A7EDB' : row.type == 2 ? '#FF473E' : '#FFC107',
padding: 6,
display: 'BYCLICK'
},
isParking: true
})
console.log(newMarkers,'newMarkersnewMarkersnewMarkers')
}
})
this.$set(this, 'markers', [...this.markers, ...newMarkers])
const validBoundaries1 = type1Data.map(row => row.boundaryStr).filter(boundary =>
typeof boundary === 'string' && boundary.trim() !== '')
const polylines1 = this.convertBoundaryToPolylines(validBoundaries1, 1)
const validBoundaries2 = type2Data.map(row => row.boundaryStr).filter(boundary =>
typeof boundary === 'string' && boundary.trim() !== '')
const polylines2 = this.convertBoundaryToPolylines(validBoundaries2, 2)
const validBoundaries3 = type3Data.map(row => row.boundaryStr).filter(boundary =>
typeof boundary === 'string' && boundary.trim() !== '')
const polylines3 = this.convertBoundaryToPolylines(validBoundaries3, 3)
this.polyline = this.polyline.concat(polylines1, polylines2, polylines3)
} else {
console.error("11111", res)
}
}).catch(error => {
console.error("22222", error)
})
},
convertBoundaryToPolyline(boundary) {
if (!boundary) return null
const points = JSON.parse(boundary).map(coord => ({
latitude: coord[1],
longitude: coord[0]
}))
const polyline = {
points: points,
fillColor: "#55888840", //填充颜色
strokeColor: "#22FF00", //描边颜色
strokeWidth: 2, //描边宽度
zIndex: 1, //层级
}
return polyline
},
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.page {
width: 750rpx;
padding-bottom: 200rpx;
box-sizing: border-box;
.center-marker {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -80%); /* 定位在中心点上方 */
pointer-events: none; /* 使其不可点击 */
}
.fixdivce {
padding: 12rpx 22rpx 12rpx 22rpx;
position: fixed;
left: 0;
top: 25vh;
width: 280rpx;
height: 40vh;
background: #FFFFFF80;
border-radius: 0 40rpx 40rpx 0;
box-shadow: 0rpx 4rpx 10rpx 0rpx rgba(0, 0, 0, 0.3);
.scrollable-content::-webkit-scrollbar {
display: none;
}
.scrollable-content {
height: 100%;
overflow-y: auto;
.divce_li:last-child {
border-bottom: 1rpx solid #fff;
}
.divce_li {
padding: 10rpx 0;
display: flex;
flex-wrap: nowrap;
align-items: center;
border-bottom: 1rpx solid #D8D8D8;
.left_img {
image {
width: 44rpx;
height: 70rpx;
}
}
.right_cont {
margin-left: 20rpx;
display: flex;
flex-wrap: wrap;
font-weight: 400;
font-size: 24rpx;
color: #3D3D3D;
.right_top {
width: 100%;
display: flex;
flex-wrap: nowrap;
align-items: center;
.right_top_left {
margin-left: auto;
display: flex;
flex-wrap: nowrap;
align-items: center;
image {
margin-right: 6rpx;
width: 12rpx;
height: 26rpx;
}
}
}
.right_bot {
width: 100%;
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
margin-top: 8rpx;
}
}
}
}
}
.decice_cont {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 10rpx 28rpx;
.cont:nth-child(5n) {
margin-right: 0;
}
.cont {
margin-top: 8rpx;
margin-right: 9rpx;
width: 130rpx;
.text {
width: 100%;
text-align: center;
font-weight: 500;
font-size: 24rpx;
line-height: 38rpx;
color: #3D3D3D;
}
.act1 {
color: #4C97E7;
}
}
}
.park {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
right: 30rpx;
bottom: 500rpx;
// background-color: #fff;
border-radius: 50%;
width: 82rpx;
height: 82rpx;
z-index: 10;
.img {
width: 82rpx;
height: 82rpx;
}
}
.btn_box {
width: 100%;
display: flex;
flex-wrap: nowrap;
.btn1 {
display: flex;
align-items: center;
justify-content: center;
width: 376rpx;
height: 74rpx;
background: #D4ECFF;
font-weight: 500;
font-size: 40rpx;
color: #4C97E7;
}
.btn2 {
display: flex;
align-items: center;
justify-content: center;
width: 374rpx;
height: 74rpx;
background: #4C97E7;
font-weight: 500;
font-size: 40rpx;
color: #FFFFFF;
}
}
.slider {
padding: 15rpx 0 0 15rpx;
margin-bottom: 20rpx;
.tit {
font-weight: 500;
font-size: 28rpx;
color: #3D3D3D;
}
width: 752rpx;
height:160rpx;
background: #FFFFFF;
// box-shadow: 0rpx 4rpx 22rpx 0rpx rgba(0, 0, 0, 0.07);
border-radius: 0rpx 0rpx 0rpx 0rpx;
}
.map {
width: 750rpx;
height: 54vh;
}
}
</style>