365 lines
11 KiB
Vue
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>
|
|
{{ $t("book.location") }}: {{ popupData.selectCity }}
|
|
</view>
|
|
<view class="header-text" @click="toList">
|
|
{{ $t("book.list") }}
|
|
<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>
|