SFH5/pages/book/mapmode.vue
2026-03-16 11:10:28 +08:00

365 lines
11 KiB
Vue

<template>
<view class="container" :class="[`${themeInfo.theme}-theme`, `${themeInfo.language}`]">
<view class="header-wrap">
<wxNavbar :title="$t('book.map')"></wxNavbar>
<!-- 头部的筛选 -->
<view class="header">
<view class="header-text" @click="open">
<uv-icon name="dropdown" custom-prefix="custom-icon" size="16" :color="themeInfo.iconColor"></uv-icon>
&nbsp;&nbsp;{{ $t("book.location") }}:&nbsp;{{ popupData.selectCity }}
</view>
<view class="header-text" @click="toList">
{{ $t("book.list") }}&nbsp;&nbsp;
<uv-icon name="setting1" custom-prefix="custom-icon" size="16" :color="themeInfo.iconColor"></uv-icon>
</view>
</view>
</view>
<!-- location窗口 -->
<uv-overlay :show="popupData.show" @click="open" z-index="99" >
<view class="location-popup">
<view class="select-area-wrap" :style="{ 'margin-top': `${state.navHeight}px` }" @click.stop.prevent>
<view class="city inner-wrap">
<view class="top-wrap">
<uv-icon name="halfArrow" custom-prefix="custom-icon" size="16" color="#0F2232"></uv-icon>
<text class="text">{{ $t("book.city") }}</text>
</view>
<view class="select-wrap">
<view
class="select-item"
v-for="(item, index) in popupData.cityData"
:key="index"
:class="{ select: item === popupData.selectCity }"
@click="handleCity(item)">
<view class="circle"></view>
<view class="name">{{ item }}</view>
</view>
</view>
</view>
<view class="border"></view>
<view class="area inner-wrap">
<view class="top-wrap">
<uv-icon name="halfArrow" custom-prefix="custom-icon" size="16" color="#0F2232"></uv-icon>
<text class="text">{{ $t("book.area") }}</text>
</view>
<view class="select-wrap">
<view
class="select-item"
v-for="(item, index) in popupData.areaData"
:key="index"
:class="{ select: item === popupData.selectDistrict }"
@click="handleDistrict(item)">
<view class="circle"></view>
<view class="name">{{ item }}</view>
</view>
</view>
</view>
</view>
</view>
</uv-overlay>
<!-- 地图 -->
<view class="mapBpx" :style="{ 'padding-top': `${state.navHeight}px` }" v-show="siteData.markerList">
<GoogleMap :markerList="siteData.markerList" :locationState="locationState" @markerClick="markerClick"></GoogleMap>
<view class="shopDetail" v-if="state.showMapDetail && siteData.selectSite?.id">
<view class="shop-detail">
<view class="close">
<uv-icon name="close" size="16" color="#969799" @click="closeMapDetail"></uv-icon>
</view>
<!-- 店铺图片 -->
<siteDetail :siteItem="siteData.selectSite"></siteDetail>
</view>
</view>
</view>
<view class="get-location-wrap" @click="handleAuthorize" v-if="locationState.showGetLocation">
{{ $t("book.get") }}
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import wxNavbar from "@/components/wxNavbar.vue";
import siteDetail from '@/components/siteDetail.vue';
import GoogleMap from "@/pages/book/map.vue";
import { ClientSite } from "/Apis/book.js";
import { getDistance, navbarHeightAndStatusBarHeight } from "@/utils/common.js";
// 主题色配置
import { useMainStore } from "@/store/index.js";
const { themeInfo } = useMainStore();
import { useLocation } from "@/hooks/useLocation";
const { locationState, openLocationAuthorize, getLocation } = useLocation();
const getApi = ClientSite();
const state = ref({
showMapDetail: true,
navHeight: 0,
firstLoad: false,
});
onMounted(() => {
state.firstLoad = true;
getCityData();
state.value.navHeight = Number(navbarHeightAndStatusBarHeight().navbarHeight) + 50;
getLocation().finally(() => {
getSiteDetail();
// #ifndef MP-WEIXIN
locationState.showGetLocation = false;
// #endif
});
});
const markerClick = (event) => {
siteData.selectSite = event.event;
// #ifdef MP-WEIXIN
if (locationState?.latitude && locationState?.longitude) {
let distanceData = getDistance(locationState.latitude, locationState.longitude, siteData.selectSite.latitude, siteData.selectSite.longitude);
siteData.selectSite.distance = distanceData.distance;
}
// #endif
state.value.showMapDetail = true;
};
const toList = () => {
uni.switchTab({
url: "/pages/book/index"
});
};
const closeMapDetail = () => {
state.value.showMapDetail = false;
};
const siteData = reactive({
list: [],
markerList: [],
selectSite: {},
isLoading: false,
});
const setMarkerList = () => {
// 只显示有经纬度的门店
let list = siteData.list.filter((item) => Number(item.latitude) && Number(item.longitude));
// 获取门店距离,底部显示最近的门店
if (locationState?.latitude && locationState?.longitude) {
list.forEach(item => {
let distanceData = getDistance(locationState.latitude, locationState.longitude, item.latitude, item.longitude);
item.distance = distanceData.distance;
item.distanceNumber = distanceData.number;
});
list.sort((a, b) => a.distanceNumber - b.distanceNumber);
}
siteData.list = list;
if (state.firstLoad) {
filterSiteData();
} else {
siteData.markerList = list.map((item) => {
return {
...item,
lat: Number(item.latitude),
lng: Number(item.longitude)
};
});
siteData.selectSite = siteData.markerList[0] || {};
}
};
// 获取门店信息
const getSiteDetail = (city, district) => {
if (siteData.isLoading) return;
// #ifdef MP-WEIXIN
// if (locationState.showGetLocation) {
// uni.showToast({
// title: t("book.getSite"),
// icon: "none",
// duration: 2000
// });
// return;
// }
// #endif
uni.showLoading();
state.isLoading = true;
getApi.getSiteDetailsAll({
city: city || "",
district: district || ""
}).then((res) => {
if (res.code === 200) {
siteData.list = res.data;
setMarkerList();
}
siteData.isLoading = false;
uni.hideLoading();
});
}
// 首次只显示距离最近的城市
const filterSiteData = () => {
state.firstLoad = false;
if (!siteData.list.length) return;
let city = siteData.list[0]['city'];
siteData.list = siteData.list.filter((item) => item.city == city);
siteData.markerList = siteData.list.map((item) => {
return {
...item,
lat: Number(item.latitude),
lng: Number(item.longitude)
};
});
siteData.selectSite = siteData.markerList[0] || {};
popupData.selectCity = city;
getApi.GetDistrictByCity(city).then(res => {
if (res.code === 200) {
popupData.areaData = res.data;
popupData.areaData.unshift("全部");
popupData.selectDistrict = popupData.areaData[0];
}
});
}
/**
* 顶部popup 相关功能
*/
const popupData = reactive({
show: false,
selectCity: "",
selectDistrict: "",
cityData: [],
areaData: [],
});
const open = () => {
popupData.show = !popupData.show;
}
const getCityData = () => {
uni.showLoading();
getApi.GetCityAll().then(res => {
if(res.code === 200) {
popupData.cityData = res.data;
popupData.cityData.unshift("全部");
// 首次直接选择全部
popupData.selectCity = popupData.cityData[0];
}
uni.hideLoading();
})
}
const handleCity = (city) => {
popupData.selectCity = city;
if (city === "全部") {
popupData.areaData = ["全部"];
popupData.selectDistrict = "全部";
getSiteDetail();
} else {
uni.showLoading();
getApi.GetDistrictByCity(city).then(res => {
if (res.code === 200) {
popupData.areaData = res.data;
popupData.areaData.unshift("全部");
popupData.selectDistrict = popupData.areaData[0];
}
uni.hideLoading();
let currentCity = city === "全部" ? "" : city;
getSiteDetail(currentCity, "");
});
}
}
const handleDistrict = (item) => {
popupData.selectDistrict = item;
let currentCity = popupData.selectCity === "全部" ? "" : popupData.selectCity;
let district = item === "全部" ? "" : item;
getSiteDetail(currentCity, district);
}
const handleAuthorize = () => {
openLocationAuthorize().then(res => {
if (res) getSiteDetail();
});
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
.header-wrap {
position: fixed;
width: 100%;
background: linear-gradient(to bottom, var(--left-linear), var(--right-linear));
z-index: 999;
::v-deep .wxNavbar {
background: transparent !important;
}
}
// 头部的筛选
.header {
height: 50px;
width: 100%;
padding: 0 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
.header-text {
font-size: 28rpx;
font-weight: bold;
display: flex;
align-items: center;
color: var(--whiteOrBlack);
& > .header-icon {
width: 28rpx;
height: 28rpx;
}
}
}
.mapBpx {
width: 100%;
height: 100vh;
position: relative;
.shopDetail {
pointer-events: none; /* 使子元素对鼠标事件透明 */
position: absolute;
bottom: 0;
margin: 0;
padding: 30rpx 0;
background: #FFFFFF;
border-radius: 20px 20px 0 0;
z-index: 9;
.close {
position: absolute;
right: 28rpx;
top: 20rpx;
}
.shop-detail {
pointer-events: all;
padding-bottom: 10rpx;
flex-wrap: wrap;
:deep(.site-detail) {
box-shadow: none;
}
}
}
}
.get-location-wrap {
margin-top: 400rpx;
width: 100%;
text-align: center;
padding: 10px 0;
border-top: 1px solid #DDDDDD;
border-bottom: 1px solid #DDDDDD;
background-color: #FFFFFF;
}
}
</style>