YingXingAI/pages/main/index/index.vue

1634 lines
57 KiB
Vue
Raw Normal View History

2025-06-30 14:43:02 +08:00
<template>
<view>
<view class="bottom">
<div class="info" v-if="showInfo">
<view class="flex-col section_2">
<view class="justify-between position-relative">
<view class="flex-row group_5">
<image @click="toDetil(detailInfo.userId)" :src="$u.http.config.imgUrl + detailInfo.imageUrl"
class="image_5"/>
<view class="name-work">
<view class="flex-row items-center h100">
<text class="text text_1">{{ detailInfo.name }}</text>
<text class="text text_3" v-if="careerList[detailInfo.workFieldName]">
<img :src="careerList[detailInfo.workFieldName].icon"
alt="">{{ detailInfo.workLableName || detailInfo.workFieldName }}
</text>
<text class="text text_3" v-else>
{{ detailInfo.workLableName }}
</text>
</view>
</view>
</view>
<view class="flex-row section_3">
<image src="/static/common/img/chats.png" class="image_6"/>
<text class="text text_2" @click="GoChat(detailInfo.userId)">打招呼</text>
</view>
</view>
<view class="flex-row tags">
<u-tag :text="detailInfo.college" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
<u-tag :text="detailInfo.major" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
<u-tag :text="detailInfo.startYear" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
</view>
<view>
<text v-if="detailInfo.aiBlurb" class="text AIdescribe">{{ detailInfo.aiBlurb }}</text>
</view>
<view class="flex-row group_6">
<image src="/static/common/img/location.svg" style="width: 32rpx;height: 32rpx;"></image>
<text class="text text_5">{{
detailInfo.province +
" " +
detailInfo.city +
" 距你 " +
(detailInfo.distance > 1000
? (detailInfo.distance / 1000).toFixed(1) + "KM"
: detailInfo.distance + "M")
}}
</text>
</view>
</view>
</div>
</view>
<view class="search-box" @click="toSearch">
<image
src="/static/common/img/search.svg"
style="width: 40rpx; height: 40rpx"
></image>
</view>
<!-- :scale="map.scale" 缩放max18 min5-->
<map
id="map"
ref="map"
:style="'width: 100vw; height: calc(100vh - .5rem );'"
:scale="mapScale"
:layer-style="'style1'"
@regionchange="regionchange"
@updated="updated"
:latitude="map.latitude"
:longitude="map.longitude"
:markers="covers"
@callouttap="de"
@markertap="getUserDetail"
@click="tap"
>
</map>
<!-- 回到本人定位位置 -->
<view class="back-to-location" @click="backToLocation"
v-if="latitude!=vuex_userLocation.latitude||longitude!=vuex_userLocation.longitude">
<image src="/static/common/img/back-to-location.png" style="width: 120rpx; height: 120rpx"></image>
</view>
<u-tabbar
:list="vuex_tabbar"
:class="{ phone: vuex_iPhone }"
:active-color="vuex_tabbar_config.activeColor"
:inactive-color="vuex_tabbar_config.inactiveColor"
></u-tabbar>
<u-toast ref="uToast"/>
<!-- 弹出列表 -->
<view class="popup-box" v-if="showPopup" :style="{
maxHeight: isExpanded,height: isExpanded,
}">
<view class="popup-top"
@click.stop="togglePopup"
@touchstart.stop.prevent="touchStart"
@touchmove.stop.prevent="touchMoveFn"
@touchend.stop.prevent="touchEnd">
<view class="pop-top"></view>
<view class="pop-title">附近校友</view>
</view>
<view class="content">
<template v-for="(item, index) in detailInfoList">
<view
v-if="item.userId == 0"
class="info"
style="
position: initial;
transform: initial;
margin-bottom: 0.1rem;
"
:key="index"
>
<view class="flex-col section_2">
<view class="justify-between position-relative">
<view class="flex-row group_5">
<image
:src="$u.http.config.imgUrl + item.imageUrl"
class="image_5"
/>
<view class="name-work">
<view class="flex-row items-center h100">
<text class="text text_1">{{ item.name }}</text>
<text class="text text_3" v-if="careerList[item.workFieldName]">
<img :src="careerList[item.workFieldName].icon"
alt="">{{ item.workLableName || item.workFieldName }}
</text>
<text class="text text_3" v-else>
{{ item.workFieldName }}
</text>
</view>
</view>
</view>
</view>
<view v-if="item.college||item.major||item.startYear" class="flex-row tags">
<u-tag :text="item.college" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
<u-tag :text="item.major" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
<u-tag :text="item.startYear" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
</view>
<view>
<text v-if="item.aiBlurb" class="text AIdescribe">{{ item.aiBlurb }}</text>
</view>
<view class="flex-row group_6">
<image
src="/static/common/img/location.svg"
style="width: 32rpx; height: 32rpx"
></image>
<text class="text text_5"
>{{
item.province +
" " +
item.city +
" 距你 " +
(item.distance > 1000
? (item.distance / 1000).toFixed(1) + "KM"
: item.distance + "M")
}}
</text>
</view>
</view>
</view>
<view
v-else
class="info"
style="
position: initial;
transform: initial;
margin-bottom: 0.1rem;
"
:key="index"
>
<view class="flex-col section_2">
<view class="justify-between position-relative">
<view class="flex-row group_5">
<u-avatar
@click="toDetil(item.userId)"
size="0.5rem"
:src="$u.http.config.imgUrl + item.imageUrl"
class="image_5"
></u-avatar>
<view class="name-work">
<view class="flex-row items-center h100">
<text class="text text_1">{{ item.name }}</text>
<text class="text text_3" v-if="careerList[item.workFieldName]">
<img :src="careerList[item.workFieldName].icon"
alt="">{{ item.workLableName || item.workFieldName }}
</text>
<text class="text text_3" v-else>
{{ item.workFieldName }}
</text>
</view>
<view>
</view>
</view>
</view>
<view @click="GoChat(item.userId)" class="flex-row section_3">
<image src="/static/common/img/chats.png" class="image_6"/>
<text class="text text_2">打招呼</text>
</view>
</view>
<view class="flex-row tags">
<u-tag :text="item.college" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
<u-tag :text="item.major" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
<u-tag :text="item.startYear" bg-color="#F6F8F9" color="#000000" mode="dark" size="mini"
type="info"></u-tag>
</view>
<view>
<text v-if="item.aiBlurb" class="text AIdescribe">{{ item.aiBlurb }}</text>
</view>
<view class="flex-row group_6">
<image
src="/static/common/img/location.svg"
style="width: 32rpx; height: 32rpx"
></image>
<text class="text text_5"
>{{
item.province +
" " +
item.city +
" 距你 " +
(item.distance > 1000
? (item.distance / 1000).toFixed(1) + "KM"
: item.distance + "M")
}}
</text>
</view>
</view>
</view>
</template>
</view>
</view>
</view>
</template>
<script>
// var jweixin = require('./../../../static/common/js/jweixin.js')
var jweixin = require("jweixin-module");
var map;
var qqmapsdk;
export default {
data() {
return {
latitude: 0, //纬度
longitude: 0,
mapScale: 16,
showInfo: false,
height: "calc(100vh - .5rem )",
width: "100vw",
// showList: true,
// viewHeight: 0,
map: {
scale: 14,
latitude: 39.909,
longitude: 116.39742,
},
// 标记点
covers: [],
pointDataList: "",
detailInfoList: [],
// 中心点
center: {
userId: 0,
latitude: 39.909, //纬度
longitude: 116.39742, //经度
iconPath: "", //显示的图标
rotate: 0, // 旋转度数
width: 50, //宽
imageUrl: "",
height: 50, //高
name: "我",
province: "",
city: "",
distance: "0",
workFieldName: "",
title: "我的位置", //标注点名
alpha: 0.8, //透明度
},
callout: {
content: '',//String 文本
color: '',//String 文本颜色
fontSize: '',// Number 文字大小
x: 3,// label的坐标原点是 marker 对应的经纬度
y: -20,// label的坐标原点是 marker 对应的经纬度
borderWidth: 0,//Number边框宽度
borderColor: '',//String 边框颜色
borderRadius: 0,//Number callout边框圆角
bgColor: '',//String 背景色
padding: '',//Number 文本边缘留白
display: 'ALWAYS',//String 'BYCLICK':点击显示; 'ALWAYS':常显
},
careerList: {
'生物/制药/化工/医疗': {
icon: '/static/common/icon/icon-1.png',
minIcon: '/static/common/icon/min-icon-1.png',
},
'政府机构/翻译/其他': {
icon: '/static/common/icon/icon-2.png',
minIcon: '/static/common/icon/min-icon-2.png',
},
'人事/行政/高级管理': {
icon: '/static/common/icon/icon-3.png',
minIcon: '/static/common/icon/min-icon-3.png',
},
'设计/市场/媒体/广告': {
icon: '/static/common/icon/icon-4.png',
minIcon: '/static/common/icon/min-icon-4.png',
},
'闽建筑/房地产': {
icon: '/static/common/icon/icon-5.png',
minIcon: '/static/common/icon/min-icon-5.png',
},
'计算机/互联网/通信/电子': {
icon: '/static/common/icon/icon-6.png',
minIcon: '/static/common/icon/min-icon-6.png',
},
'服务业': {
icon: '/static/common/icon/icon-7.png',
minIcon: '/static/common/icon/min-icon-7.png',
},
'教育/培训': {
icon: '/static/common/icon/icon-8.png',
minIcon: '/static/common/icon/min-icon-8.png',
},
'销售/客服': {
icon: '/static/common/icon/icon-9.png',
minIcon: '/static/common/icon/min-icon-9.png',
},
'资讯/法律/科研': {
icon: '/static/common/icon/icon-10.png',
minIcon: '/static/common/icon/min-icon-10.png',
},
'会计/金融/银行/保险': {
icon: '/static/common/icon/icon-11.png',
minIcon: '/static/common/icon/min-icon-11.png',
},
'生产/营运/采购/物流': {
icon: '/static/common/icon/icon-12.png',
minIcon: '/static/common/icon/min-icon-12.png',
},
'学生': {
icon: '/static/common/icon/icon-13.png',
minIcon: '/static/common/icon/min-icon-13.png',
},
'教师': {
icon: '/static/common/icon/icon-14.png',
minIcon: '/static/common/icon/min-icon-14.png',
},
},
list: [],
load: true,
detailInfo: "",
Showip: "",
isExpanded: '0vh',
touchStartY: 0,
startScrollTop: 0,
showPopup: false,
timeer: null
};
},
onLoad(e) {
// if(!this.vuex_user.isCard){
// uni.navigateTo({
// url: '/pages/Face/index/index'
// });
// return
// }
// if (!this.vuex_user.isFill) {
// uni.navigateTo({
// url: "/pages/login/perfect/perfect",
// });
// return
// } else {
// this.$u.api.getUser()
// }
console.info("🚀 ~ file:index method:onLoad line:374 -----", this.vuex_user)
this.center.imageUrl = this.vuex_user.head;
this.center.workField = this.vuex_user?.workField?.workFieldName || "暂无";
this.center.workFieldName = this.vuex_user?.workFieldName || "暂无";
this.center.aiBlurb = this.vuex_user?.aiBlurb || "暂无";
this.center.aiLabel = this.vuex_user?.aiLabel || "";
this.center.college = this.vuex_user?.college || "";
this.center.major = this.vuex_user?.major || "";
this.center.startYear = this.vuex_user?.startYear || "";
// 创建地图上下文
map = uni.createMapContext("map", this);
uni.getSystemInfo({
success: (res) => {
this.height = res.windowHeight + "px";
this.width = res.windowWidth + "px";
},
});
// this.location();
// qqmapsdk = new QQMapWX({
// key: '2OLBZ-OOSRQ-RYZ5A-GMCM2-DJ43O-3QFLS', //开发者密钥
// })
// console.log(this.vuex_user.isFill)
},
onShow() {
/* uni.getSystemInfo({
success: (res) => {
this.viewHeight = res.windowHeight;
},
}); */
this.map.latitude = this.vuex_userLocation.latitude;
this.map.longitude = this.vuex_userLocation.longitude;
this.center.latitude = this.vuex_userLocation.latitude;
this.center.longitude = this.vuex_userLocation.longitude;
setTimeout(() => {
if (!this.map.latitude || !this.map.longitude) {
this.location(true);
}
this.test();
}, 500);
if (!this.vuex_user.isFill) {
uni.navigateTo({
url: "/pages/login/perfect/perfect",
});
return;
} else {
this.$u.api.getUser();
}
},
methods: {
// 回到当前位置
backToLocation() {
this.location(true);
map.moveToLocation({
latitude: this.vuex_userLocation.latitude || this.center.latitude,
longitude: this.vuex_userLocation.longitude || this.center.longitude,
});
this.latitude = this.vuex_userLocation.latitude || this.center.latitude;
this.longitude = this.vuex_userLocation.longitude || this.center.longitude;
},
// 查看详情
toDetil(id) {
this.$u.route({
url: "/pages/AlumniCircle/userDetail/userDetail?id=" + id,
});
},
// 去聊天
GoChat(id) {
this.showInfo = false;
uni.navigateTo({
url:
"../../message/dialogBox/dialogBox?id=" + id + "&chatType=0&type=0",
});
},
toSearch() {
if (this.vuex_user.isAttestationXY || true) {
this.$u.route({
url: "/pages/main/search/search",
params: {
type: 'index',
},
});
} else {
this.$refs.uToast.show({
title: "认证后才能进行搜索哦",
url: "/pages/my/ShoolList/ShoolList",
});
}
},
test() {
map.getRegion({
success: (res) => {
this.Showip = {
northeast: res.northeast,
southwest: res.southwest,
};
this.getList();
},
});
},
getEpsByScale(zoomLevel) {
// 缩放等级范围限制在 1 到 18
if (zoomLevel < 1) zoomLevel = 1;
if (zoomLevel > 18) zoomLevel = 18;
// 基础距离(单位:米),通常在缩放等级 0 时显示整个地球(约 40075 公里)
const earthCircumference = 40075000; // 地球赤道周长(米)
// 根据缩放等级计算当前等级下的单个瓦片的地面宽度
const tileSize = 256; // 地图瓦片大小(通常是 256x256 像素)
const scale = 2 ** zoomLevel; // 缩放等级对应的缩放比例
// 计算每个像素代表的地面距离(米/像素)
const metersPerPixel = earthCircumference / (tileSize * scale);
// 返回一个以 50 像素为参考的距离(可以根据需求调整这个值)
const pixelRange = 50; // 聚合点之间的最小像素距离
return metersPerPixel * pixelRange;
},
//根据东北 西南经纬度 以及后台返回标记点 格式化成marker点
async getFortMatMarkerList(northeast, southwest, scale, backendMarkerList) {
// 当前显示的坐标范围
// console.log(southwest.longitude, northeast.longitude, northeast.latitude, southwest.latitude)
//屏幕中显示的经度的长度和纬度的长度
let mapWidth = southwest.longitude - northeast.longitude;
let mapHeight = northeast.latitude - southwest.latitude;
//将屏幕中地图分割的横向 格子数和 纵向格子数
// let widthSize = scale;
// let heightSize = widthSize + parseInt(scale / 2);
let widthSize = 30 - parseInt(scale / 2);
let heightSize = 30 - parseInt(scale / 2);
//计算每个格子的经纬度的长度
let unitWidth = mapWidth / widthSize;
let unitHeight = mapHeight / heightSize;
// console.log(southwest.longitude - northeast.longitude, unitHeight)
let pointData = {};
backendMarkerList.forEach((latLng) => {
//如果点在显示范围内
if (
latLng.latitude < northeast.latitude &&
latLng.latitude > southwest.latitude &&
latLng.longitude < northeast.longitude &&
latLng.longitude > southwest.longitude
) {
let relativeX = latLng.longitude - northeast.longitude;
let relativeY = latLng.latitude - southwest.latitude;
//计算出这个点,处于哪个格子
let x = parseInt(Math.floor(relativeX / unitWidth));
let y = parseInt(Math.floor(relativeY / unitHeight));
if (x < 0 || y < 0) {
console.log("点位不在格子内", "失败");
}
//生成单元格点位数据
let pointKey = x + "," + y;
if (pointData[pointKey] == undefined) {
pointData[pointKey] = [];
}
if (pointData[pointKey].length <= 99) {
pointData[pointKey].push(latLng);
} else {
return
}
} else {
console.log("点位不在显示范围内", "失败");
}
});
console.info("🚀 ~ file:index method:getFortMatMarkerList line:547 -----", pointData)
// ===== 新增:使用 DBSCAN 进行全局点聚合 start =====
const eps = this.getEpsByScale(scale); // 根据缩放级别调整 eps 值
pointData = this.dbscan(pointData, parseInt(eps)); // 聚合参数可调整
this.pointDataList = pointData;
let resultMapArray = [];
for (let y = 0; y < heightSize; y++) {
for (let x = 0; x < widthSize; x++) {
let pointKey = x + "," + y;
if (
pointData[pointKey] != undefined &&
(pointData[pointKey][0].userId ||
pointData[pointKey][0].userId === 0)
) {
let markerItem = {};
//聚合点与单点共存 , 长度等于一 不聚合点
if (pointData[pointKey].length == 1) {
var HeadImg = "/static/common/img/adminHeaderImg.png?number=1";
if (pointData[pointKey][0].userId == 0) {
if (this.vuex_user.sex == "男") {
HeadImg = "/static/common/img/male.png?number=1";
}
if (this.vuex_user.sex == "女") {
HeadImg = "/static/common/img/female.png?number=1";
}
}
// 请求在线图片文件 判断是否存在
// const imgExist = await this.doesImageExist(this.$u.http.config.imgUrl +
// pointData[pointKey][0].imageUrl +
// "?number=1");
//
// if (imgExist) {
// HeadImg =
// this.$u.http.config.imgUrl +
// pointData[pointKey][0].imageUrl +
// "?number=1";
// }
// 线上会出现奇怪的问题,所以先注释掉
if (pointData[pointKey][0].imageUrl != "") {
HeadImg =
this.$u.http.config.imgUrl +
pointData[pointKey][0].imageUrl +
"?number=1";
}
markerItem = {
id: parseInt(
(pointData[pointKey][0].latitude -
-pointData[pointKey][0].longitude) *
100000
),
latitude: pointData[pointKey][0].latitude,
longitude: pointData[pointKey][0].longitude,
iconPath: HeadImg,
width: 50,
height: 50,
callout: {
content: scale == 18 ? pointData[pointKey][0].aiLabel : '',
display: 'ALWAYS',
borderRadius: 100,
bgColor: '#3CB5FB',
color: '#fff',
borderColor: '#3CB5FB',
},
label: {
...this.callout,
width: 50,
height: 50,
content: !this.careerList[pointData[pointKey][0].workFieldName] ? `` : `<img src="${this.careerList[pointData[pointKey][0].workFieldName].minIcon}">`
}
};
if (pointData[pointKey][0].userId === 0) {
markerItem.id = '0'
if (!pointData[pointKey][0].aiLabel) {
markerItem.title = '我的位置'
}
}
// let iconPath = pointData[pointKey][0].ScanAndCharge == 1 ? '/img/scanMarkerIcon.png' : '/img/markerIcon.png';
//长度大于一聚合点
} else if (pointData[pointKey].length > 1) {
// console.log('聚合点', pointData[pointKey]);
// id会被转为number类型字符类型不能触发点击时间
// 最后面加1是为了在结尾为0的情况时0在转被为number不被裁剪掉
// 对比判断id时去掉加上的 1
if (pointData[pointKey].length > 99) {
pointData[pointKey] = pointData[pointKey].slice(0, 99)
}
markerItem = {
id: pointKey.split(",")[0] + "." + pointKey.split(",")[1] + "1",
latitude: pointData[pointKey][0].latitude,
longitude: pointData[pointKey][0].longitude,
iconPath: await this.generateGroupAvatar(pointData[pointKey]), // 使用生成的群组头像
width: 50,
height: 50,
list: pointData[pointKey],
};
}
resultMapArray.push(markerItem);
}
}
}
return resultMapArray;
}
,
async doesImageExist(url) {
try {
const response = await fetch(url, {method: 'HEAD'});
return response.ok;
} catch (error) {
console.error('图片不存在或发生错误:', error);
return false;
}
},
generateGroupAvatar(users) {
return new Promise((resolve, reject) => {
try {
const canvasSize = 100;
const maxAvatars = 9;
const filteredUsers = users.slice(0, maxAvatars);
const total = filteredUsers.length;
if (total === 0) {
resolve('/static/common/img/map_icon.png');
return;
}
// 缓存 key
const userIds = filteredUsers.map(u => u.userId).join(',');
const cacheKey = `groupAvatar_${userIds}`;
if (this[cacheKey]) {
resolve(this[cacheKey]);
return;
}
// 创建 Canvas
const canvas = document.createElement('canvas');
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvasSize, canvasSize);
let drawnCount = 0;
const padding = 2;
const verticalPadding = 3;
// 布局配置
const layoutConfig = {
1: {rows: 1, cols: 1, centeredRows: [0]},
2: {rows: 1, cols: 2}, // 横向排列,但垂直居中
3: {rows: 2, cols: [1, 2], centeredRows: [0]}, // 上1 下2上居中
4: {rows: 2, cols: 2}, // 2x2 四宫格
5: {rows: 2, cols: [2, 3], centeredRows: [0]}, // 上2 下3上居中
6: {rows: 2, cols: 3}, // 上3 下3
7: {rows: 3, cols: [1, 3, 3], centeredRows: [0]}, // 上1 中3 下3
8: {rows: 3, cols: [2, 3, 3], centeredRows: [0]}, // 上2 中3 下3
9: {rows: 3, cols: 3},
};
const config = layoutConfig[total] || layoutConfig[9];
const {rows, cols} = config;
const colCounts = Array.isArray(cols) ? cols : Array(rows).fill(cols);
const centeredRows = config.centeredRows || [];
// 计算最大列数,用于计算每个头像宽度
const maxCols = Math.max(...colCounts);
const avatarWidth = (canvasSize - padding * (maxCols - 1)) / maxCols;
const rowHeight = (canvasSize - verticalPadding * (rows - 1)) / rows;
// 绘制每个头像
filteredUsers.forEach((user, index) => {
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = this.$u.http.config.imgUrl + user.imageUrl;
// 确定当前头像属于哪一行
let currentRow = 0;
let count = 0;
for (let i = 0; i < rows; i++) {
const colCount = colCounts[i];
if (index < count + colCount) {
currentRow = i;
break;
}
count += colCount;
}
const colIndex = index - count;
const currentColCount = colCounts[currentRow];
// 如果该行需要居中则计算起始X坐标
const startX = centeredRows.includes(currentRow)
? (canvasSize - currentColCount * (avatarWidth + padding) + padding) / 2
: 0;
const x = startX + colIndex * (avatarWidth + padding);
const y = currentRow * (rowHeight + verticalPadding);
// 固定绘制为正方形,避免拉伸
const drawSize = Math.min(avatarWidth, rowHeight);
// 居中绘制
const offsetY = (rowHeight - drawSize) / 2;
image.onload = () => {
ctx.save();
ctx.beginPath();
ctx.rect(x, y + offsetY, drawSize, drawSize);
ctx.clip();
// 从图片中心裁剪出一个正方形
const imgRatio = image.width / image.height;
const cropSize = imgRatio > 1 ? image.height : image.width;
const offsetX = (image.width - cropSize) / 2;
const offsetYImg = (image.height - cropSize) / 2;
ctx.drawImage(
image,
offsetX, offsetYImg, cropSize, cropSize, // 源图裁剪区域
x, y + offsetY, drawSize, drawSize // 目标绘制区域
);
ctx.restore();
drawnCount++;
if (drawnCount === filteredUsers.length) {
// 如果超过最大数量,覆盖整个画布并居中显示 "+n"
if (users.length > maxAvatars) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
ctx.beginPath();
ctx.rect(0, 0, canvasSize, canvasSize);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 40px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`+${users.length}`, canvasSize / 2, canvasSize / 2);
}
// 缓存结果并返回
const result = canvas.toDataURL('image/png');
this[cacheKey] = result;
resolve(result); // 异步完成
}
};
image.onerror = (err) => {
image.src = "data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
image.onload = () => {
ctx.save();
ctx.beginPath();
ctx.rect(x, y + offsetY, drawSize, drawSize);
ctx.clip();
// 从图片中心裁剪出一个正方形
const imgRatio = image.width / image.height;
const cropSize = imgRatio > 1 ? image.height : image.width;
const offsetX = (image.width - cropSize) / 2;
const offsetYImg = (image.height - cropSize) / 2;
ctx.drawImage(
image,
offsetX, offsetYImg, cropSize, cropSize, // 源图裁剪区域
x, y + offsetY, drawSize, drawSize // 目标绘制区域
);
ctx.restore();
drawnCount++;
if (drawnCount === filteredUsers.length) {
// 如果超过最大数量,覆盖整个画布并居中显示 "+n"
if (users.length > maxAvatars) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
ctx.beginPath();
ctx.rect(0, 0, canvasSize, canvasSize);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 40px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`+${users.length}`, canvasSize / 2, canvasSize / 2);
}
// 缓存结果并返回
const result = canvas.toDataURL('image/png');
this[cacheKey] = result;
resolve(result); // 异步完成
}
}
};
});
} catch (error) {
console.error('生成聚合头像时发生异常:', error);
resolve('/static/common/img/map_icon.png');
}
});
}
,
/**
* DBSCAN 聚类算法输出格式与 pointData 一致
* @param points 原始点数组 { latitude, longitude }
* @param eps 聚类距离阈值单位
* @returns {{}} 统一格式的 pointData 结构key 格式为 "x,y"
*/
dbscan(points, eps = 50) {
function getDistanceInMeters(lat1, lon1, lat2, lon2) {
const R = 6371; // 地球半径(公里)
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distanceInKm = R * c;
const distanceInMeters = distanceInKm * 1000; // 将公里转换为米
console.info("🚀 ~ file:index method:getDistanceInMeters line:886 -----", distanceInMeters)
return parseInt(distanceInMeters) > 100000 ? 100000 : parseInt(distanceInMeters);
}
let deletedPoints = [];
for (let key in points) {
if (deletedPoints.includes(key)) {
continue;
}
let X = key.split(',')[0]
let Y = key.split(',')[1]
for (let keyTwo in points) {
let innerX = keyTwo.split(',')[0]
let innerY = keyTwo.split(',')[1]
if (key === keyTwo) {
continue
}
if (Math.abs(X - innerX) <= 1 && Math.abs(Y - innerY) <= 1) {
if (getDistanceInMeters(points[key][0].latitude, points[key][0].longitude, points[keyTwo][0].latitude, points[keyTwo][0].longitude) < eps) {
points[key] = points[key].concat(points[keyTwo])
deletedPoints.push(keyTwo)
}
}
}
}
for (let key in points) {
if (deletedPoints.includes(key)) {
delete points[key]
}
}
return points;
// ===== 构造统一格式 end =====
}
,
/**
* 将聚类结果转为类似 pointData 的结构"x,y": []
* @param clusters 聚类结果二维数组
* @param noise 噪声点
* @returns [[]] 合并后的伪格子结构
*/
_generateFakeGrids(clusters, noise) {
const result = [...clusters]; // 每个 cluster 单独作为一个格子
// 如果需要可选地将噪声点也作为一个格子加入
if (noise.length > 0) {
result.push(noise); // 可选
}
return result;
}
,
updated() {
// console.log("渲染完成");
}
,
// 当视野发生改变
regionchange(e) {
// console.log("当视野发生改变");
// 视野移动结束再获取数据
if (e.type == "begin") return;
// 关闭显示的窗口
this.showInfo = false
this.test()
// let loadDelay = 200;
// if (this.load) {
// // console.log("等待加载");
// } else {
// this.load = true;
// setTimeout(() => {
// // this.getList();
// this.test();
// }, loadDelay);
// }
}
,
isWechat() {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/micromessenger/i) == "micromessenger") {
// console.log(‘是微信客户端’)
return true;
} else {
// console.log(‘不是微信客户端’)
return false;
}
}
,
//在需要定位页面调用
getlocation: function (callback) {
if (!this.isWechat()) {
//console.log('不是微信客户端')
return;
}
}
,
//打开位置
openlocation: function (data) {
if (!this.isWechat()) {
//console.log('不是微信客户端')
return;
}
jweixin.ready(function () {
jweixin.openLocation({
latitude: data.latitude,
longitude: data.longitude,
name: data.name,
address: data.address,
scale: 14,
});
});
}
,
//定位当前
location(state) {
var that = this;
uni.showLoading({
title: '正在加载...',
mask: true
})
if (state && this.isWechat()) {
const isiOS = !!navigator.userAgent.match(
/\(i[^;]+;( U;)? CPU.+Mac OS X/
); //ios终端
// 进行签名的时候 Android 不用使用之前的链接, ios 需要
const signLink = isiOS
? window.entryUrl
: window.location.href.split("#")[0];
//获取当前url然后传递给后台获取授权和签名信息后台需要解码才能使用
// const url =encodeURIComponent(signLink);
const url = signLink;
const data = {
url: url,
};
this.$u.api.GetInfoMation(data).then((res) => {
jweixin.config({
debug: false,
appId: res.appId,
timestamp: res.timestamp,
nonceStr: res.noncestr,
signature: res.signature,
jsApiList: [
//这里是需要用到的接口名称
"getLocation", //获取位置
"openLocation", //打开位置
],
});
// jweixin.config 执行失败时调用
jweixin.error((err) => {
uni.hideLoading();
that.location(false);
console.log("授权失败,您可能无法使用部分功能", err);
});
jweixin.ready(function () {
jweixin.getLocation({
type: "gcj02", // 默认为wgs84的gps坐标如果要返 回直接给openLocation用的火星坐标可传入'gcj02'
success: (res) => {
uni.hideLoading();
var resdata = res;
// console.log(res,'success');
map.moveToLocation({
latitude: resdata.latitude,
longitude: resdata.longitude,
});
// alert(JSON.stringify(res))
that.center.latitude = resdata.latitude; //纬度
that.center.longitude = resdata.longitude; //经度
// 更新坐标
var data = {
userId: that.vuex_user.id,
longitude: resdata.longitude + "",
latitude: resdata.latitude + "",
};
that.$u.api.upPosition(data).then(() => {
that.getList(resdata.latitude, resdata.longitude);
that.test();
});
},
fail: function (res) {
uni.hideLoading();
// console.log(res, "err");
},
complete: function (res) {
uni.hideLoading();
// console.log(res, "is");
},
});
});
});
} else {
uni.getLocation({
// type: "wgs84 ",
type: "gcj02 ",
isHighAccuracy: true,
highAccuracyExpireTime: 3000,
success: (res) => {
uni.hideLoading();
// console.info("🚀 ~ file:index method:success line:497 -----", res);
// console.log(res.latitude + "," + res.longitude, "gcj02");
map.moveToLocation({
latitude: res.latitude,
longitude: res.longitude,
});
this.center.latitude = res.latitude; //纬度
this.center.longitude = res.longitude; //经度
// 更新坐标
var data = {
userId: this.vuex_user.id,
longitude: res.longitude + "",
latitude: res.latitude + "",
};
this.$u.api.upPosition(data).then(() => {
this.getList(res.latitude, res.longitude);
this.test();
});
},
fail: (err) => {
uni.hideLoading();
console.log("地理位置获取失败", err);
},
});
}
}
,
testlist() {
// this.getList();
this.test();
}
,
// 获取用户列表
getList(la = 0, lo = 0) {
var arr = [];
map.getCenterLocation({
success: (res) => {
let latitude = res.latitude;
let longitude = res.longitude;
if (la !== 0) {
latitude = la;
longitude = lo;
}
// console.info("🚀 ~ file:index method:success line:1038 -----", latitude,longitude)
this.latitude = latitude;
this.longitude = longitude;
let data = {
userId: this.vuex_user.id,
lat: this.center.latitude,
lon: this.center.longitude,
nort_lat: this.Showip?.northeast?.latitude || 0,
nort_lon: this.Showip?.northeast?.longitude || 0,
sout_lat: this.Showip?.southwest?.latitude || 0,
sout_lon: this.Showip?.southwest?.longitude || 0,
};
if (this.timeer) {
clearTimeout(this.timeer)
}
this.timeer = setTimeout(() => {
this.$u.api.HomeMap(data).then((res) => {
if (res) {
res.unshift(this.center);
// res = res.reverse();
this.list = res;
// this.test();
} else {
/* uni.showToast({
title: res.data.msg,
icon: "none",
}); */
}
map.getScale({
success: async (ress) => {
this.covers = await this.getFortMatMarkerList(
this.Showip.northeast,
this.Showip.southwest,
ress.scale,
this.list
);
document.querySelectorAll(
"#map .csssprite:nth-child(1)"
)[0].style.border = "none";
},
});
}).catch(() => {
this.list = [this.center];
map.getScale({
success: async (ress) => {
this.covers = await this.getFortMatMarkerList(
this.Showip.northeast,
this.Showip.southwest,
ress.scale,
this.list
);
document.querySelectorAll(
"#map .csssprite:nth-child(1)"
)[0].style.border = "none";
},
});
});
}, 500)
},
});
}
,
tap() {
this.showInfo = false;
}
,
// 点击获取当前用户信息 注意:<每个 marker 点都必须要有id 不然点击maxker的事件不会触发>
getUserDetail(i) {
// console.info(
// "🚀 ~ file:index method:getUserDetail line:574 -----",
// i.detail.markerId
// );
//点击中心点时不显示
if (i.detail.markerId == 0) return;
// 判断是否是聚合点
this.detailInfoList = [];
if (String(i.detail.markerId).split(".").length > 1) {
var pointKey = String(i.detail.markerId).replace(".", ",");
pointKey = pointKey.slice(0, pointKey.length - 1);
let arr = this.pointDataList[pointKey];
let isprovince = "";
let iscity = "";
// 地址解析 免费 每日10000请求量
uni.showLoading({
title: "加载中",
});
this.$jsonp(
"https://apis.map.qq.com/ws/geocoder/v1/?key=2OLBZ-OOSRQ-RYZ5A-GMCM2-DJ43O-3QFLS&location=" +
arr[0].latitude +
"," +
arr[0].longitude +
"&output=jsonp&get_poi=1"
).then((res) => {
if (res.status == 0) {
isprovince = res.result.address_component.province;
iscity = res.result.address_component.city;
for (let i in arr) {
arr[i].province = isprovince ? isprovince : "未";
arr[i].city = iscity ? iscity : "知";
}
this.detailInfoList = arr;
this.isExpanded = '0vh';
this.showPopup = true
setTimeout(() => {
this.isExpanded = '90vh';
}, 10);
// this.showList();
uni.hideLoading();
}
});
} else {
// 根据id筛选获取用户信息
let a = this.list.filter((item) => {
return (
parseInt((item.latitude - -item.longitude) * 100000) ==
i.detail.markerId
);
})[0];
if (a.userId == this.detailInfo.userId) {
setTimeout(() => {
this.showInfo = true;
this.showPopup = false
}, 100);
return;
}
// 地址解析
this.$jsonp(
"https://apis.map.qq.com/ws/geocoder/v1/?key=2OLBZ-OOSRQ-RYZ5A-GMCM2-DJ43O-3QFLS&location=" +
a.latitude +
"," +
a.longitude +
"&output=jsonp&get_poi=1"
).then((res) => {
// console.log("解析地址成功", res);
// console.log("当前地址:", res.result.address);
if (res.status == 0) {
a.province = res.result.address_component.province;
a.city = res.result.address_component.city;
}
this.detailInfo = a;
setTimeout(() => {
this.showInfo = true;
this.showPopup = false
}, 100);
});
}
}
,
togglePopup() {
this.isExpanded = '90vh';
}
,
touchStart(e) {
// 记录起始触摸点
this.touchStartY = e.touches[0].clientY;
// 记录起始滚动位置
this.startScrollTop = e.currentTarget.scrollTop;
}
,
touchMove(e) {
const currentY = e.touches[0].clientY;
const diff = this.touchStartY - currentY;
const scrollTop = e.currentTarget.scrollTop;
// 判断是否在内容顶部或底部
const isAtTop = scrollTop <= 0;
const isAtBottom = scrollTop + e.currentTarget.clientHeight >= e.currentTarget.scrollHeight;
// 在顶部向下滑动或在底部向上滑动时,阻止默认行为
if ((isAtTop && diff < 0) || (isAtBottom && diff > 0)) {
e.preventDefault();
}
// 向上滑动超过50px时展开
if (diff > 50) {
this.isExpanded = '90vh';
}
// 向下滑动超过50px时收起
else if (diff < -50) {
if (this.isExpanded == '40vh') {
console.log("关闭");
this.isExpanded = '0vh';
setTimeout(() => {
this.showPopup = false
}, 100);
} else {
console.log("收起");
this.isExpanded = '40vh';
}
}
}
,
touchMoveFn(e) {
if (this.timeer) {
clearTimeout(this.timeer)
}
this.timeer = setTimeout(() => {
this.touchMove(e)
}, 100)
}
,
touchEnd(e) {
// 可以在这里添加滑动结束后的处理逻辑
}
,
},
};
</script>
<style scoped lang="scss">
.AIdescribe {
display: block;
font-size: 26rpx;
color: rgba(0, 0, 0, 0.4);
line-height: 30rpx;
text-align: left;
margin-top: 20rpx;
max-width: 67vw;
white-space: break-spaces;
}
.tags {
gap: 10rpx;
margin-top: 20rpx;
}
.h100 {
height: 100%
}
.search-box {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 50rpx;
left: 50rpx;
z-index: 1;
box-shadow: 0rpx 10rpx 10rpx -6rpx rgba(0, 0, 0, 0.1),
0rpx 16rpx 20rpx 2rpx rgba(0, 0, 0, 0.06),
0rpx 6rpx 28rpx 4rpx rgba(0, 0, 0, 0.05);
}
::v-deep .uni-scroll-view-content {
background: #f6f8f9 !important;
}
::v-deep #map .csssprite {
border-radius: 50%;
border: 2px solid #358ddf !important;
filter: drop-shadow(0px 0px 2px #358ddf);
transform: scale(0.8);
}
::v-deep #map > div > div > div > div > div:nth-last-child(4) > div > div {
overflow: initial !important;
}
// ::v-deep #map>div>div>div>div>div:nth-last-child(4)>div>div:nth-child(1)>.csssprite[src=''] {
// border: none !important;
// }
.name-work {
display: flex;
flex-direction: column;
padding-left: 20rpx;
}
.info {
position: absolute;
right: 0;
left: 0;
transform: translateY(-125%);
.section_2 {
padding: 0.11rem 0.18rem 0.15rem 0.21rem;
background-color: rgb(255, 255, 255);
box-shadow: 0px 0px 0.1rem rgba(0, 0, 0, 0.1);
border-radius: 0.1rem;
.text_3 {
color: #3CB5FB;
font-size: 26rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-left: 10rpx;
vertical-align: middle;
img {
vertical-align: middle;
Width: 48rpx;
}
}
.group_6 {
white-space: nowrap;
border-top: 1px solid #F4F6F8;
padding-top: 20rpx;
margin-top: 20rpx;
.text_4 {
margin-right: 0.1rem;
color: rgb(63, 63, 63);
font-size: 0.14rem;
line-height: 0.16rem;
white-space: nowrap;
}
.text_5 {
font-size: 28rpx;
color: rgba(0, 0, 0, 0.6);
line-height: 0.16rem;
letter-spacing: 0.014rem;
white-space: nowrap;
}
}
.group_5 {
color: rgb(56, 58, 63);
font-size: 0.18rem;
line-height: 0.17rem;
white-space: nowrap;
.image_5 {
width: 0.44rem;
height: 0.44rem;
border-radius: 50%;
}
.text_1 {
font-size: 30rpx;
// margin-left: 0.13rem;
//margin-top: 22rpx;
}
}
.section_3 {
padding: 20rpx 20rpx;
color: rgb(255, 255, 255);
font-size: 24rpx;
white-space: nowrap;
background-color: #3cb5fb;
border-radius: 0.15rem;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
.image_6 {
width: 32rpx;
height: 32rpx;
}
.text_2 {
margin-left: 0.055rem;
}
}
// .text {
// text-transform: uppercase;
// }
}
}
.bottom {
position: fixed;
z-index: 10;
bottom: 0;
right: 2%;
left: 2%;
padding-bottom: 0.48rem;
.btn {
display: flex;
justify-content: center;
margin-top: 0.22rem;
}
.section_4 {
margin-right: 0.025rem;
margin-top: 0.17rem;
padding: 0.2rem 0.2rem 0.3rem;
color: rgb(193, 196, 204);
font-size: 0.14rem;
line-height: 0.14rem;
white-space: nowrap;
background-color: rgb(255, 255, 255);
box-shadow: 0px 0px 0.03rem rgba(0, 0, 0, 0.1);
border-radius: 0.1rem 0.1rem 0 0;
.search {
margin-left: 0.14rem;
margin-right: 0.1rem;
padding: 0.14rem 0 0.13rem;
background-color: rgb(246, 247, 250);
border-radius: 0.05rem;
// .text {
// text-transform: uppercase;
// }
}
}
}
.popup-box {
position: fixed;
left: 0;
right: 0;
bottom: 0;
min-height: 240rpx;
box-shadow: 0rpx -14rpx 16rpx 0rpx rgba(0, 0, 0, 0.06);
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
transition: all 0.3s ease;
z-index: 100;
}
.popup-top {
// position: fixed;
// top: 0;
width: 100%;
padding-top: 20rpx;
background: #fff;
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
cursor: pointer; // 添加手型光标
}
.pop-top {
width: 88rpx;
height: 10rpx;
background: #9ea1b9;
border-radius: 200rpx;
margin: 20rpx auto 0;
}
.pop-title {
padding: 20rpx 32rpx 40rpx;
font-weight: 600;
font-size: 32rpx;
color: #191919;
background: #fff;
}
.content {
height: calc(100% - 174rpx) !important;
overflow-y: auto;
background: #f6f8f9;
padding: 30rpx 30rpx 50rpx;
// -webkit-overflow-scrolling: touch;
}
.back-to-location {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 180rpx;
right: 50rpx;
z-index: 1;
}
::v-deep #map > div:nth-child(1) > div > div:nth-child(1) > div > div:nth-child(2) {
z-index: 999999 !important;
}
::v-deep #map a[title="到腾讯地图查看此区域"] {
display: none !important;
}
.position-relative {
position: relative;
}
</style>