first commit

This commit is contained in:
2026-02-26 09:32:03 +08:00
commit 36a8e4c51b
845 changed files with 116474 additions and 0 deletions
@@ -0,0 +1,455 @@
<template>
<z-paging>
<template #top>
<navbar/>
<view class="bg-white px-30rpx py-26rpx">
<view class="flex items-center h-86rpx px-28rpx bg-#F6F7F9 rounded-20rpx">
<wd-input v-model="mapSearchKeyword" :no-border="true" :placeholder="t('common.placeholder.pleaseEnter')"
custom-class="!w-full !bg-transparent" placeholder-class="text-#9B9CA0 text-28rpx">
<template #prefix>
<view class="w-12rpx h-12rpx rounded-50% bg-#F97C34"></view>
</template>
</wd-input>
</view>
<view class="" @click="handleUseLocation">
<view class="text-32rpx text-#333 font-500 my-30rpx">{{ t('common.useMyCurrentLocation') }}</view>
<view class="text-28rpx text-#333 font-500 flex items-center pb-20rpx pl-24rpx">
<view class="i-carbon:location-current text-32rpx mr-14rpx mt-4rpx"></view>
{{ t('common.useCurrentLocation') }}
</view>
</view>
</view>
</template>
<view :change:prop="mapRenderjs.searchPlace" :prop="querySearch" class="bg-#f5f5f5">
<view class="px-22rpx py-20rpx">
<template v-if="placesLength === 0">
<view class="center py-100rpx">
<image class="w-250rpx h-250rpx" src="@img/chef/100.png"></image>
</view>
</template>
<template v-else>
<view class="rounded-36rpx bg-white">
<template v-for="(item,index) in logicStore.placesList" :key="index">
<view :class="[index === logicStore.placesList.length - 1 ? '' : 'border-bottom']"
class="px-22rpx pb-30rpx pt-34rpx"
@click="handleClickLocation(item)">
<!-- <view >{{ item }}</view> -->
<view class="text-#000 text-26rpx font-bold">{{ item.displayName }}</view>
<view class="text-#9B9CA0 text-24rpx flex-center-sb">
<view>{{ item.formattedAddress }}</view>
</view>
</view>
</template>
</view>
</template>
</view>
<view id="map" :change:prop="mapRenderjs.initMap" :prop="mapDataComputed" :user-location="userLocation"
class="absolute left-0 bottom-0 w-0 h-0"></view>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import Config from "@/config";
import {EventEnum} from "@/constant/enums";
import {useLogicStore} from "@/pages-user/store/logic";
import {useUserStore} from "@/store";
const {t, locale} = useI18n();
const userStore = useUserStore()
const logicStore = useLogicStore()
// 生命周期:清空地址列表
onMounted(() => {
logicStore.clearPlacesList()
})
const placesLength = computed(() => {
return logicStore.placesList.length;
});
const userLocation = computed(() => ({
longitude: userStore.location.longitude,
latitude: userStore.location.latitude
}));
watch(
() => logicStore.searchLoading,
(newValue) => {
if (newValue) {
uni.showLoading({
title: 'Loading...',
mask: true,
});
} else {
uni.hideLoading();
}
},
{immediate: true}
);
// 地图搜索关键词
const mapSearchKeyword = ref<string>('');
const querySearch = computed(() => {
return {
keyword: mapSearchKeyword.value,
}
})
function handleClickLocation(item: any) {
console.log('item', item)
uni.$emit(EventEnum.CHOOSE_ADDRESS, item)
uni.navigateBack()
}
// 使用当前位置
function handleUseLocation() {
// 检查是否获取到了当前定位
if (!userStore.location.longitude || !userStore.location.latitude) {
// 尝试再次获取定位
uni.getLocation({
isHighAccuracy: true,
type: 'gcj02',
geocode: true,
success: async (res) => {
getCityName(res.latitude, res.longitude)
},
fail: (err) => {
const settings = uni.getAppAuthorizeSetting()
console.log(settings)
if (settings.locationAuthorized === 'denied') {
uni.showToast({
title: t('common.prompt.please-authorize-location-information'),
icon: 'none'
})
setTimeout(()=> {
uni.openAppAuthorizeSetting()
})
}
},
})
} else {
uni.$emit(EventEnum.CHOOSE_ADDRESS, {
displayName: userStore.location.location,
formattedAddress: userStore.location.formattedAddress || '',
location: {
lng: userStore.location.longitude,
lat: userStore.location.latitude
}
})
uni.navigateBack()
}
}
function getCityName(latitude: number, longitude: number) {
const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${Config.googleMapKey}&language=${locale.value === 'zh-Hans' ? 'zh-CN' : locale.value}`;
uni.request({
url,
method: 'GET',
timeout: 10000,
success: (res: any) => {
const results = res.data?.results || [];
console.log('geocode results:', results);
if (!results.length) {
return handleFail();
}
const addr = results[0]; // 最高匹配度
const components = addr.address_components || [];
// 提取城市名的工具函数
const pickAddress = (types: string[]) => {
return components.find((item) => item.types.some((t) => types.includes(t)));
};
// 寻找城市名(多层兜底)
let cityObj =
pickAddress(["locality"]) ||
pickAddress(["administrative_area_level_2"]) ||
pickAddress(["administrative_area_level_1"]) ||
pickAddress(["political"]);
const cityName = cityObj?.long_name || null;
// 从 Google 获取完整地址
const formattedAddress = addr.formatted_address || cityName || "";
console.log("city:", cityObj);
console.log("formattedAddress:", formattedAddress);
if (!cityName) {
return handleFail();
}
// 存入 store
userStore.location = {
location: cityName,
formattedAddress,
longitude,
latitude
};
// 发事件
uni.$emit(EventEnum.CHOOSE_ADDRESS, {
displayName: cityName,
formattedAddress,
location: { lng: longitude, lat: latitude }
});
uni.navigateBack();
},
fail: () => handleFail()
});
function handleFail() {
uni.showToast({
title: t('common.prompt.getLocationFailed'),
icon: 'none'
});
}
}
const mapDataComputed = computed(() => {
return {
language: locale.value,
lng: userStore.location.longitude || 174.7633,
lat: userStore.location.latitude || -36.8485,
}
})
// 监听来自 renderjs 的事件
onMounted(() => {
// 监听搜索结果
uni.$on('MAP_SEARCH_RESULT', (places: any) => {
console.log(places, '接收到的搜索结果')
if (places && places.length > 0) {
// 解析实际返回的数据结构
const parsedPlaces = places.map((placeArray: any) => {
const placeData = placeArray[0]?.[0];
if (!placeData) return null;
const placeId = placeData[1];
const formattedAddress = placeData[8] || '';
const locationArray = placeData[11];
const displayNameArray = placeData[27];
return {
id: placeId,
displayName: displayNameArray?.[0] || formattedAddress,
formattedAddress: formattedAddress,
location: locationArray ? {
lat: locationArray[0],
lng: locationArray[1]
} : null
};
}).filter(Boolean);
console.log('解析后的地址列表', parsedPlaces);
logicStore.setPlacesList(parsedPlaces);
} else {
logicStore.setPlacesList([]);
}
})
// 监听地图未加载提示
uni.$on('MAP_NOT_LOADED', () => {
uni.showToast({
title: 'Map is not loaded yet, please wait',
icon: 'none'
});
})
// 监听搜索超时提示
uni.$on('MAP_SEARCH_TIMEOUT', () => {
uni.showToast({
title: 'Search timeout, please try again',
icon: 'none'
});
})
// 监听搜索加载状态
uni.$on('MAP_SEARCH_LOADING', (isLoading: boolean) => {
// logicStore.searchLoading = isLoading;
})
})
onUnmounted(() => {
// 清理事件监听
uni.$off('MAP_SEARCH_RESULT')
uni.$off('MAP_NOT_LOADED')
uni.$off('MAP_SEARCH_TIMEOUT')
uni.$off('MAP_SEARCH_LOADING')
})
</script>
<script lang="renderjs" module="mapRenderjs">
import {Loader} from "@googlemaps/js-api-loader"
import * as R from 'ramda'
import Config from '@/config'
import {debounce} from "throttle-debounce";
let map = null
let mapLoaded = false; // 地图加载完成标志
// @ts-ignore
export default {
data() {
return {
region: "US",
lan: 'en',
google: null,
canvas: null,
center: null,
dotLottie: null,
marker: null,
markerViewList: [],
AdvancedMarkerElement: null,
lng: -77.0365,
lat: 38.8977,
userLocation: {
longitude: 0,
latitude: 0
}
}
},
props: {
userLocation: Object
},
mounted() {
console.log('mounted mapRenderjs')
mapLoaded = false; // 重置地图加载完成标志
// !map&&this.initMap()
this.searchPlace = debounce(200, this.searchPlace)
},
methods: {
initMap({lng, lat, language}) {
console.log('initMap', lng, lat)
console.log('当前系统语言', language)
this.lan = language === 'zh-Hans' ? 'zh-CN' : 'en'
this.region = language === 'zh-Hans' ? 'CN' : 'US'
this.lng = language === 'zh-Hans' ? 116.4074 : -77.0365
this.lat = language === 'zh-Hans' ? 39.9042 : 38.8977
console.log('当前系统语言', this.lan)
console.log('当前系统区域', this.region)
this.$nextTick(() => {
const loader = new Loader({
apiKey: Config.googleMapKey,
version: "weekly",
region: this.region, // 设置为美国
language: this.lan,
});
loader.load().then(async (google) => {
const {Map} = await google.maps.importLibrary("maps")
this.google = google
console.log('google', google.maps)
// 地图相关配置
// 指定自定义地图样式的 ID。
// center: 地图初始中心点的经纬度(lat, lng)。
// zoom: 地图初始缩放级别。
// fullscreenControl: 是否显示全屏按钮(false 表示不显示)。
// cameraControl: 是否显示相机控制(false 表示不显示)。
// disableDefaultUI: 是否禁用所有默认 UI 控件(true 表示全部禁用)。
// gestureHandling: 设置手势操作方式("greedy" 表示允许所有手势操作)
const mapOptions = {
mapId: 'ff2268c265ce7a40',
center: {
lat: this.lat,
lng: this.lng,
},
zoom: 12,
fullscreenControl: false,
cameraControl: false,
disableDefaultUI: true,
gestureHandling: "greedy",
};
map = new Map(document.getElementById("map"), mapOptions);
mapLoaded = true; // 地图加载完成
});
})
},
async searchPlace({keyword}) {
console.log('搜索关键词', keyword);
if (!keyword) {
uni.$emit('MAP_SEARCH_RESULT', [])
return;
}
if (!mapLoaded) {
console.log('地图未加载完成,无法搜索');
uni.$emit('MAP_NOT_LOADED');
return;
}
if (!map || !this.google) {
return
}
uni.$emit('MAP_SEARCH_LOADING', true);
let timeoutId;
try {
// 设置搜索超时
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
uni.$emit('MAP_SEARCH_LOADING', false);
reject(new Error('Search timeout'));
}, 10000); // 10 秒超时
});
const {Place, SearchByTextRankPreference} = await this.google.maps.importLibrary("places");
const request = {
textQuery: keyword,
fields: ['id', 'displayName', 'location', 'formattedAddress', 'addressComponents'],
locationBias: {lat: this.lat, lng: this.lng},
language: this.lan,
maxResultCount: 20,
region: this.region,
rankPreference: SearchByTextRankPreference.RELEVANCE
};
const searchPromise = Place.searchByText(request);
const {places} = await Promise.race([searchPromise, timeoutPromise]);
console.log('地图搜索结果原始数据', places);
// 将 Google Places API 返回的对象转换为可序列化的数组格式
const serializedPlaces = places.map(place => {
// 提取需要的字段并转换为普通对象
return [[{
1: place.id,
8: place.formattedAddress || '',
9: place.addressComponents || [],
11: place.location ? [place.location.lat(), place.location.lng()] : null,
27: place.displayName ? [
typeof place.displayName === 'string' ? place.displayName : place.displayName.text,
place.displayName.languageCode || this.lan
] : null
}]];
});
console.log('序列化后的搜索结果', serializedPlaces);
uni.$emit('MAP_SEARCH_RESULT', serializedPlaces);
} catch (e) {
console.log('搜索错误原因', e);
if (e.message === 'Search timeout') {
uni.$emit('MAP_SEARCH_TIMEOUT');
}
uni.$emit('MAP_SEARCH_RESULT', []);
} finally {
clearTimeout(timeoutId);
uni.$emit('MAP_SEARCH_LOADING', false);
}
}
},
}
</script>
<style>
page {
background-color: #f5f5f5;
}
</style>
<style lang="scss" scoped>
</style>