<template> <view class="page"> <u-navbar title="门店选择" :border-bottom="false" :background="bgc" title-color='#000' back-icon-color="#000" title-size='44' height='40'></u-navbar> <view class="serch"> <input type="text" v-model="keyword" @confirm="confirmss" placeholder="搜索门店"/> <view class="dt" v-if="!dtlbflag" @click="btnlbtop"> <image src="https://api.ccttiot.com/smartmeter/img/static/u2uM05aGZDeVxrdrNmoh" mode=""></image> 地图 </view> <view class="lb" v-else @click="btndttop"> <image src="https://api.ccttiot.com/smartmeter/img/static/ubAi251bnYEIdAkwJXcA" mode=""></image> 列表 </view> </view> <view class="dtxs"> <map class='map' id="map" :latitude="latitude" :longitude="longitude" @markertap="handleMarkerClick" :show-location="true" :markers="covers" :scale="mapScale" @regionchange="regionchange"/> <image class="picimg" v-if="iconflag" src="https://api.ccttiot.com/smartmeter/img/static/uEAmNMMt65U10qwijrsJ" mode=""></image> </view> <!-- 0000000000000000000000000000000000000000000 --> <!-- <view class="listbox" style="background: transparent;" > --> <view class="list" v-if="listobj.name" @click="btnxqstore"> <view class="pic"> <image :src="listobj.picture.split(',')[0]" mode="aspectFill"></image> </view> <view class="cen"> <view class="cena" style="font-weight: 600;"> {{listobj.name}} </view> <view class="cenb"> <image src="https://api.ccttiot.com/smartmeter/img/static/uHK2aJHvzEm3nbOPwpdr" mode="aspectFit"></image> {{listobj.address.length > 15 ? listobj.address.slice(0,13) + '...' :listobj.address}} </view> <view class="cenc"> 营业时间:{{listobj.businessTimeStart}}-{{listobj.businessTimeEnd}} </view> <view class="" style=" display: flex; align-items: center; justify-content: space-between; width: 100%;margin-top: 24rpx;"> <view class="juli"> 距我{{listobj.distance}} </view> <view class="right"> <image src="https://api.ccttiot.com/smartmeter/img/static/u4Q5lajsFuAv7YEzBFib" mode="aspectFit" @click.stop="mapFuns(item)"></image> <image src="https://api.ccttiot.com/smartmeter/img/static/uPeCVStFHIF0rAhMKhlK" mode="aspectFit" @click.stop="btntel(item)"></image> </view> </view> </view> </view> <!-- </view> --> <!-- 0000000000000000000000000000000000000000000 --> <view class="listbox" v-if="!dtlbflag"> <view class="list" v-for="(item,index) in gxlist" :key="index"> <view class="pic" @click="btndts(item)"> <image :src="item.picture.split(',')[0]" mode="aspectFill"></image> </view> <view class="cen" @click="btndts(item)"> <view class="cena" style="font-weight: 600;"> {{item.name}} </view> <view class="cenb"> <image src="https://api.ccttiot.com/smartmeter/img/static/uHK2aJHvzEm3nbOPwpdr" mode="aspectFit"></image> {{item.address.length > 15 ? item.address.slice(0,13) + '...' :item.address}} </view> <view class="cenc"> 营业时间:{{item.businessTimeStart}}-{{item.businessTimeEnd}} </view> <view class="" style=" display: flex; align-items: center; justify-content: space-between; width: 100%;margin-top: 24rpx;"> <view class="juli"> 距我{{item.distance}} </view> <view class="right"> <image src="https://api.ccttiot.com/smartmeter/img/static/u4Q5lajsFuAv7YEzBFib" mode="aspectFit" @click.stop="mapFun(item)"></image> <image src="https://api.ccttiot.com/smartmeter/img/static/uPeCVStFHIF0rAhMKhlK" mode="aspectFit" @click.stop="btntel(item)"></image> </view> </view> </view> </view> <view class="" v-if="gxlist.length == 0" style="width: 100%;height: 200rpx;margin: auto;margin-top: 230rpx;text-align: center;"> <image style="width: 200rpx;height: 200rpx;" src="https://api.ccttiot.com/smartmeter/img/static/uZFUpcz0YZZ4f4RjvGg2" mode="aspectFit"></image> <view class="" style="font-size: 28rpx;color: #808080;margin-top: 30rpx;">该区域暂无店铺...</view> </view> </view> <view class="saoma" @click="scanQRCode"> <view> <image src="https://api.ccttiot.com/smartmeter/img/static/uBh15vzRx6gV0wRQj7gi" mode="aspectFit"></image> 扫一扫 </view> </view> </view> </template> <script> export default { data() { return { latitude: '', longitude: '', covers: [], index: 1, isMch: false, gxlist: [], bgc: { backgroundColor: " #fff", }, mapScale: 15, mapContext: null, mapScaleInterval: null, listmap: [], iconflag: false, dtlbflag:true, listobj:{}, keyword:'' } }, onLoad() { }, onShow() { this.getshanghu() }, onReady() { this.mapContext = uni.createMapContext('map', this) this.mapScaleInterval = setInterval(this.updateMarkers, 1000) }, // 分享到好友(会话) onShareAppMessage: function () { return { title: '创想物联', path: '/pages/shouye/index' } }, // 分享到朋友圈 onShareTimeline: function () { return { title: '创想物联', query: '', path: '/pages/shouye/index' } }, beforeDestroy() { // 在组件卸载前清除定时器 if (this.mapScaleInterval) { clearInterval(this.mapScaleInterval) this.mapScaleInterval = null } }, methods: { confirmss(){ this.covers = [] this.gxlist = [] this.$u.get("/app/store/listNearBy?center=" + this.jinweidu + '&radius=' + 20000 + '&keyword=' + this.keyword).then( response => { if (response.code == 200) { this.listmap = response.data let gxlist = response.data gxlist.forEach(item => { const distance = this.getDistance(this.latitude, this.longitude, item.lat, item.lng) item.distance = distance const shopCover = { id: parseFloat(item.storeId), latitude: item.lat, longitude: item.lng, width: 25, height: 30, iconPath: 'https://api.ccttiot.com/smartmeter/img/static/uZXgsJE6hXbRSwbKILB4', distance: distance, // 可以在这里也添加距离到覆盖物对象中,但通常只需要在店铺对象中 label: { content: item.name, anchorX: (() => { if (item.name.length <= 2) { return -12 } else if (item.name.length <= 3) { return -20 } else if (item.name.length <= 4) { return -25 } else if (item.name.length <= 5) { return -30 } else if (item.name.length <= 6) { return -35 } else if (item.name.length <= 8) { return -40 } else if (item.name.length <= 10) { return -45 } else { return -item.name.length * 5 } })(), fontWeight: 700, color: '#8883F0', textShadow: '2px 2px 0px white, -2px -2px 0px white, 2px -2px 0px white, -2px 2px 0px white', borderRadius:5, bgColor: '#fff' } } this.covers.push(shopCover) this.gxlist.push(item) }) } }) }, // 扫描二维码 scanQRCode() { uni.scanCode({ onlyFromCamera: true, scanType: ['qrCode'], success: 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) let id = getQueryParam(decodedValue, 's') let that = this let data = { deviceNo: id } that.$u.get(`/app/device/isBind?deviceNo=${id}`).then(res => { if (res.data == 2) { that.$u.get(`/app/device/${id}/withSuitList`).then((res) => { if (res.code == 200) { uni.navigateTo({ url: '/page_components/fuwu/index?id=' + id }) } }) } else if (res.data == 1) { if(that.phonenumber == ''){ that.huoqutelflag = true that.xuanzeflag = false }else{ that.xuanzeflag = false uni.showModal({ title: '提示', content: '该设备未绑定,你需进行绑定吗?', success: function(res) { if (res.confirm) { that.$u.put("/app/device/bind", data).then(res => { if (res.code == 200) { that.$u.get(`/app/device/${id}/bySn`).then((res) => { if (res.code == 200) { uni.navigateTo({ url: '/page_components/bindsz?id=' + res.data.deviceId }) } }) } }) } } }) } } else if (res.data == 0) { uni.showModal({ title: '提示', content: '该设备未录入,请先进行录入', success: function(res) { if (res.confirm) { // uni.navigateTo({ // url: '/page_fenbao/zhuce?sn=' + id // }) } else if (res.cancel) { } } }) } }) }, fail: err => { console.error('扫描失败:', err) uni.showToast({ title: '扫描失败', icon: 'none' }) } }) }, // 点击列表显示地图 btnlbtop(){ this.dtlbflag = true this.listobj = {} }, // 点击地图显示列表 btndttop(){ this.dtlbflag = false this.listobj = {} }, // 点击拨打电话 btntel(item){ uni.makePhoneCall({ phoneNumber: this.listobj.contactMobile, success: function(res) { console.log('拨打电话成功', res) }, fail: function(err) { console.error('拨打电话失败', err) uni.showToast({ title: '取消拨打电话', icon: 'none' }) } }) }, // 查询拖动到指定为止的店铺 regionchange(e) { this.iconflag = true if (e.type == 'end') { this.jinweidu = e.detail.centerLocation.longitude + ',' + e.detail.centerLocation.latitude this.$u.get("/app/store/listNearBy?center=" + this.jinweidu + '&radius=' + 20000).then(res => { if (res.code == 200) { this.covers = [] this.listmap = res.data this.gxlist = res.data this.gxlist.forEach(item => { // 计算距离 const distance = this.getDistance(this.latitude, this.longitude, item.lat, item.lng) // 将距离添加到店铺对象中 item.distance = distance const shopCover = { id: parseFloat(item.storeId), latitude: item.lat, longitude: item.lng, width: 25, height: 30, iconPath: 'https://api.ccttiot.com/smartmeter/img/static/uZXgsJE6hXbRSwbKILB4', distance: distance, // 可以在这里也添加距离到覆盖物对象中,但通常只需要在店铺对象中 // iconPath:item.picture label: { content: item.name, anchorX: this.calculateAnchorX(item.name), fontWeight: 700, color: '#8883F0', borderColor: '#fff', borderRadius: 5, bgColor: '#fff' } } this.covers.push(shopCover) }) } }) this.iconflag = false } else { // this.iconflag = false } }, updateMarkers() { this.mapContext.getScale({ success: (res) => { this.covers = [] // 清空之前的覆盖物 if (res.scale <= 14) { this.addMarkersWithoutLabels() } else { this.addMarkersWithLabels() } }, fail: (error) => { // console.error('获取地图缩放级别失败:', error) // 额外的错误处理逻辑... }, }); }, addMarkersWithoutLabels() { this.listmap.forEach((item) => { // if (item.deviceCount !== null && item.deviceCount > 0) { const shopCover = { id: parseFloat(item.storeId), latitude: item.lat, longitude: item.lng, width: 25, height: 30, iconPath: 'https://api.ccttiot.com/smartmeter/img/static/uZXgsJE6hXbRSwbKILB4', borderRadius:5, bgColor: '#fff' } this.covers.push(shopCover) // } }) }, addMarkersWithLabels() { this.listmap.forEach((item) => { // if (item.deviceCount !== null && item.deviceCount > 0) { const shopCover = { id: parseFloat(item.storeId), latitude: item.lat, longitude: item.lng, width: 25, height: 30, iconPath: 'https://api.ccttiot.com/smartmeter/img/static/uZXgsJE6hXbRSwbKILB4', label: { content: item.name, anchorX: this.calculateAnchorX(item.name), fontWeight: 700, color: '#8883F0', textShadow: '2px 2px 0px white, -2px -2px 0px white, 2px -2px 0px white, -2px 2px 0px white', borderRadius:5, bgColor: '#fff' } } this.covers.push(shopCover) // } }) }, calculateAnchorX(name) { let chineseLength = 0 let englishLength = 0 for (let i = 0; i < name.length; i++) { const charCode = name.charCodeAt(i) // 判断是否为中文字符(这里简化了判断,只考虑了基本的中文字符范围) if (charCode >= 0x4e00 && charCode <= 0x9fa5) { chineseLength++ } else if (/[a-zA-Z]/.test(name[i])) { // 判断是否为英文字符 englishLength = englishLength + 0.3 // 英文长度加1,但视为两个中文字符长度 // console.log(englishLength) } } const totalLength = chineseLength + englishLength * 2 // 总长度(以中文字符为单位) return -totalLength * 6.5 // 假设每个中文字符对应的 anchorX 偏移是 -6.5 }, onControltap(control) { this.setMapScale() }, // 地图回正 async setMapScale(e, val) { let mapContext = uni.createMapContext('map', this); let setScale = () => { return new Promise((resolve, reject) => { mapContext.getScale({ success: r => { this.mapScale = 15 resolve() } }) }) }; await setScale(); mapContext.moveToLocation({ success: (res) => { const timer = setTimeout(() => { this.mapScale = 15 clearTimeout(timer) }, 500) }, }) }, // 跳转导航 mapFun(item) { uni.openLocation({ latitude: item.lat, longitude: item.lng, name: item.county, address: item.address }) }, mapFuns() { uni.openLocation({ latitude: this.listobj.lat, longitude: this.listobj.lng, name: this.listobj.county, address: this.listobj.address }) }, getshanghu() { this.$u.get("/app/user/userInfo").then((res) => { if (res.code == 200) { this.isMch = res.data.isMch } }) }, getMyLocation() { uni.getLocation({ type: 'wgs84', success: (res) => { this.jinweidu = res.longitude + ',' + res.latitude this.latitude = Number(res.latitude.toFixed(5)) - 0.005 this.longitude = Number(res.longitude.toFixed(5)) + 0.005 this.setMapScale() // 请求附近的店铺 this.$u.get("/app/store/listNearBy?center=" + this.jinweidu + '&radius=' + 20000).then( response => { if (response.code == 200) { this.listmap = response.data let gxlist = response.data gxlist.forEach(item => { // if (item.deviceCount !== null && item.deviceCount > 0) { // 计算距离 const distance = this.getDistance(this.latitude, this.longitude, item.lat, item.lng) // 将距离添加到店铺对象中 item.distance = distance // 创建店铺覆盖物对象 const shopCover = { id: parseFloat(item.storeId), latitude: item.lat, longitude: item.lng, width: 25, height: 30, iconPath: 'https://api.ccttiot.com/smartmeter/img/static/uZXgsJE6hXbRSwbKILB4', distance: distance, // 可以在这里也添加距离到覆盖物对象中,但通常只需要在店铺对象中 label: { content: item.name, anchorX: (() => { if (item.name.length <= 2) { return -12 } else if (item.name.length <= 3) { return -20 } else if (item.name.length <= 4) { return -25 } else if (item.name.length <= 5) { return -30 } else if (item.name.length <= 6) { return -35 } else if (item.name.length <= 8) { return -40 } else if (item.name.length <= 10) { return -45 } else { return -item.name.length * 5 } })(), fontWeight: 700, color: '#8883F0', textShadow: '2px 2px 0px white, -2px -2px 0px white, 2px -2px 0px white, -2px 2px 0px white', borderRadius:5, bgColor: '#fff' } } this.covers.push(shopCover) this.gxlist.push(item) // } }) } }).catch(error => { console.error('请求店铺列表失败:', error) }) }, fail: (err) => { console.error('获取位置失败:', err) } }); }, // 计算距离函数 getDistance(lat1, lon1, lat2, lon2) { const R = 6371000; // 地球半径,单位:米 const phi1 = lat1 * Math.PI / 180; // 纬度转为弧度 const phi2 = lat2 * Math.PI / 180; const deltaPhi = (lat2 - lat1) * Math.PI / 180; const deltaLambda = (lon2 - lon1) * Math.PI / 180; const a = Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) + Math.cos(phi1) * Math.cos(phi2) * Math.sin(deltaLambda / 2) * Math.sin(deltaLambda / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const distance = R * c; // 判断距离并格式化输出 if (distance > 1000) { // 大于1000米,转换为千米并保留一位小数 return (distance / 1000).toFixed(1) + 'Km'; } else { // 小于等于1000米,直接用米表示 return Math.floor(distance) + 'm'; } }, handleMarkerClick(event) { const markerId = event.markerId // uni.navigateTo({ // url: '/page_user/mapditu/index?markerId=' + markerId // }) this.$u.get("/app/store/" + markerId).then(res => { if(res.code == 200){ const distance = this.getDistance(this.latitude, this.longitude, res.data.lat, res.data.lng) this.listobj = { ...res.data, distance: distance } } }) }, btndts(item) { const markerId = item.storeId uni.navigateTo({ url: '/page_user/mapditu/index?markerId=' + markerId }) }, // 点击进入详情 btnxqstore(){ uni.navigateTo({ url: '/page_user/mapditu/index?markerId=' + this.listobj.storeId }) }, btntab(num) { this.index = num }, btndt(num) { if (num == 1) { uni.navigateTo({ url: '/page_user/mapditu/index' }) } else if (num == 2) { // console.log(111); uni.switchTab({ url: '/pages/index/index' }) } } }, mounted() { this.getMyLocation() } } </script> <style lang="scss"> /deep/ .u-title { padding-bottom: 2rpx; } /deep/ .u-icon__icon { padding-bottom: 2rpx; } /deep/ .map { width: 100%; height: 100%; } .serch{ width: 100%; height: 130rpx; background: #FFFFFF; padding: 0 18rpx; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; input{ width: 630rpx; height: 78rpx; line-height: 78rpx; box-sizing: border-box; padding-left: 20rpx; background-color: #F2F3F5; } view{ text-align: center; font-size: 22rpx; color: #3D3D3D; width: 60rpx; image{ width: 42rpx; height: 42rpx; } } } page { background: linear-gradient(180deg, #D9D8FF 0%, rgba(255, 255, 255, 0) 100%); border-radius: 0rpx 0rpx 0rpx 0rpx; } .saoma { width: 750rpx; height: 166rpx; background: #FFFFFF; position: fixed; left: 0; bottom: 0; padding-top: 22rpx; border-radius: 54rpx 54rpx 0 0; margin-top: 20rpx; view { text-align: center; width: 676rpx; height: 102rpx; background: linear-gradient(270deg, #8883F0 0%, #8883F0 100%); border-radius: 54rpx 54rpx 54rpx 54rpx; font-weight: 500; font-size: 40rpx; color: #FFFFFF; margin: auto; line-height: 102rpx; } image { width: 56rpx; height: 56rpx; vertical-align: middle; margin-right: 10rpx; margin-bottom: 10rpx; } } .page { width: 750rpx; position: fixed; top: 0; left: 0; .dtxs { width: 100%; height: 100vh; position: fixed; top: 0; left: 0; z-index: -1; .picimg { width: 120rpx; height: 120rpx; position: absolute; left: 50%; transform: translateX(-50%); top: 50%; } } .list { display: flex; margin-top: 10px; border-bottom: 1px solid #eee; padding: 16rpx 14rpx; box-sizing: border-box; background-color: #fff; width: 690rpx; margin: auto; margin-top: 20rpx; height: 250rpx; border-radius: 12rpx; .pic { width: 210rpx; height: 210rpx; border-radius: 10rpx; margin-right: 20rpx; border-radius: 20rpx; image { width: 210rpx; height: 210rpx; border-radius: 20rpx; } } .cen { width: 488rpx; .cena { font-weight: 700 !important; font-size: 28rpx; color: #3D3D3D; line-height: 38rpx; } .cenb { font-weight: 400; font-size: 26rpx; color: #3D3D3D; line-height: 32rpx; margin-top: 14rpx; padding-right: 30rpx; box-sizing: border-box; image { display: inline-block; width: 22rpx; height: 22rpx; margin-right: 6rpx; } } .juli{ font-size: 24rpx; padding: 6rpx 10rpx; box-sizing: border-box; border: 1px solid #4D48B5; color: #4D48B5; width: 160rpx; text-align: center; background: #EFEEFF; border-radius: 10rpx; } .cenc { font-weight: 400; font-size: 26rpx; color: #3D3D3D; line-height: 32rpx; margin-top: 14rpx; text { display: inline-block; padding: 4rpx 18rpx; box-sizing: border-box; border-radius: 20rpx; } .bu { margin-left: 32rpx; background: #D9D8FF; color: #4D48B5; font-size: 20rpx; } } } .right { image { width: 48rpx; height: 48rpx; margin-right: 10rpx; } text { display: block; font-weight: 400; font-size: 20rpx; color: #808080; line-height: 28rpx; width: 100rpx; } } } .listbox { background: #eee; box-shadow: 0rpx 10rpx 64rpx 0rpx rgba(0, 0, 0, 0.08); padding-left: 32rpx; padding-right: 32rpx; box-sizing: border-box; height: 81vh; overflow-y: scroll; padding-bottom: 400rpx; .moshi { position: absolute; bottom: 40vh; left: 30rpx; z-index: 99; width: 100%; image { width: 230rpx; // height: 80rpx !important; } .img { height: 50rpx; margin-bottom: 8rpx; } } .title { border-radius: 64rpx 64rpx 0 0; display: flex; justify-content: space-between; font-weight: 500; font-size: 36rpx; color: #3D3D3D; line-height: 160rpx; padding: 0 76rpx; width: 100%; background-color: #fff; position: fixed; height: 160rpx; top: 40vh; left: 0; padding-bottom: 20rpx; } } } .active { border-bottom: 10rpx solid #D9D8FF; border-radius: 7rpx; padding-bottom: 10rpx; font-weight: 700; } </style>