Files
ks-app-employment-service/packageRc/components/placePicker.vue
2025-11-04 15:16:22 +08:00

884 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<uni-popup ref="visible">
<view style="position: relative;height: 100vh;width: 100vw;">
<!-- 搜索容器 -->
<view class="search-container">
<!-- 搜索区域 -->
<view class="search-area">
<!-- 搜索框带图标 -->
<view class="search-input-wrapper">
<!-- <u-icon name="search" class="search-icon" size="28rpx" color="#999999"></u-icon> -->
<input
v-model="placeInput"
@focus="onInputFocus"
@blur="onInputBlur"
@input="onInputChange"
placeholder="请输入并选择相应地点"
placeholder-style="color: #CCCCCC"
clearable
@clear="onInputClear"
/>
<!-- 搜索加载动画 -->
<!-- <u-loading v-if="isSearching" size="24rpx" class="search-loading"></u-loading> -->
</view>
<!-- 搜索历史记录 -->
<!-- <view v-if="showHistory && placeInput === ''" class="search-history">
<view class="history-title">搜索历史</view>
<view class="history-list">
<view
v-for="(item, index) in searchHistory"
:key="index"
class="history-item"
@tap="searchHistoryItem(item)"
>
<u-icon name="clock" size="24rpx" color="#999999" class="history-icon"></u-icon>
<text>{{ item }}</text>
添加单个删除按钮
<u-icon
name="close"
size="24rpx"
color="#CCCCCC"
class="delete-icon"
@tap.stop="deleteHistoryItem(index)"
></u-icon>
</view>
<view v-if="searchHistory.length === 0" class="empty-history">暂无搜索记录</view>
</view>
</view> -->
<!-- 热门搜索 -->
<view v-if="showHotSearch && placeInput === '' && !showHistory" class="hot-search">
<view class="hot-title">热门搜索</view>
<view class="hot-tags">
<view v-for="(tag, index) in hotSearchTags" :key="index" class="hot-tag" @tap="searchHotTag(tag)">
{{ tag }}
</view>
</view>
</view>
<!-- 已选择的地点 -->
<view v-if="checkedMarker && checkedMarker.name" class="selected">
<view class="selected-content">
<text class="selected-text">您已选择{{ checkedMarker.name }}</text>
<text v-if="checkedMarker.address" class="selected-address">{{ checkedMarker.address }}</text>
</view>
<view class="clear-btn" @tap="resetSelection">清除</view>
</view>
<!-- 搜索结果列表 -->
<view v-if="placeList.length > 0" class="search-results">
<view class="results-title">找到 {{ placeList.length }} 个相关地点</view>
<view
v-for="(item, index) in placeList"
:key="index"
class="place-item"
@tap="addIcon(item.name)"
>
<view class="item-content">
<view class="item-name">{{ item.name }}</view>
<view class="item-address">{{ item.address }}</view>
</view>
<!-- <u-icon name="arrow-right" size="24rpx" color="#CCCCCC" class="item-arrow"></u-icon> -->
</view>
</view>
<!-- 无结果提示 -->
</view>
</view>
<!-- 地图组件 -->
<map
id="map"
class="map"
:latitude="mapCenter.latitude"
:longitude="mapCenter.longitude"
:markers="markers"
:scale="15"
show-location
@markertap="onMarkerTap"
@regionchange="onRegionChange"
@tap="onMapTap"
></map>
<!-- 底部按钮 -->
<view class="button-area">
<view class="btn" @tap="cancel"> </view>
<view class="btn save" @tap="submitForm"> </view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
// 请修改saveToSearchHistory和loadSearchHistory方法添加异常处理
export default {
data() {
return {
placeList: [],
placeInput: '',
checkedMarker: '',
visible: false,
// 地图中心点(济南市)
mapCenter: {
latitude: 36.657017,
longitude: 117.123237
},
// 地图标记点
markers: [],
// 当前选中的标记点
currentMarker: null,
// 搜索状态
isSearching: false,
showHistory: true,
showHotSearch: true,
// 搜索历史
searchHistory: [],
// 热门搜索标签
hotSearchTags: ['济南西站', '泉城广场', '大明湖', '千佛山', '济南东站', '融创文旅城']
}
},
mounted() {
// 获取用户位置权限
this.getUserLocation();
// 加载搜索历史
this.loadSearchHistory();
},
methods: {
deleteHistoryItem(index) {
// 删除指定索引的历史记录
this.searchHistory.splice(index, 1);
// 更新本地存储
uni.setStorageSync('placeSearchHistory', this.searchHistory);
},
// 获取用户位置
getUserLocation() {
uni.getLocation({
type: 'gcj02',
success: (res) => {
this.mapCenter = {
latitude: res.latitude,
longitude: res.longitude
};
},
fail: (err) => {
console.log('获取位置失败:', err);
// 使用默认位置(济南市)
}
});
},
submitForm() {
if(this.checkedMarker && this.checkedMarker.name){
this.$emit('selected', this.checkedMarker)
// this.visible = false;
this.$refs.visible.close();
}else{
uni.showToast({
title: '您尚未选择地点!',
icon: 'none'
});
}
},
cancel() {
this.$refs.visible.close();
// this.visible = false;
},
openDialog() {
this.$refs.visible.open();
// this.visible = true;
this.placeInput = '';
this.placeList = [];
this.checkedMarker = '';
this.markers = [];
this.isSearching = false;
// this.showHistory = true;
this.showHotSearch = true;
},
// 重置选择
resetSelection() {
this.checkedMarker = '';
this.markers = [];
uni.showToast({
title: '已清除选择',
icon: 'none',
duration: 1500
});
},
// 搜索框聚焦
onInputFocus() {
// this.showHistory = true;
this.showHotSearch = true;
},
// 搜索框失焦
onInputBlur(e) {
// 失焦时保持搜索结果显示
console.log("e",e); // 保留调试信息
},
// 输入变化
onInputChange(e) {
// 安全地获取输入值,处理各种可能的情况
let value = '';
if (!e) {
value = '';
} else if (e.detail && e.detail.value !== undefined) {
value = e.detail.value;
} else if (e.target && e.target.value !== undefined) {
value = e.target.value;
} else if (typeof e === 'string') {
value = e;
}
this.placeInput = value;
if (value.trim() === '') {
this.placeList = [];
// this.showHistory = true;
this.showHotSearch = true;
return;
}
// 延迟搜索,避免频繁请求
if (this.searchTimer) {
clearTimeout(this.searchTimer);
}
this.searchTimer = setTimeout(() => {
this.getLocations(value);
}, 500);
},
// 清除输入框
onInputClear() {
this.placeInput = '';
this.placeList = [];
// this.showHistory = true;
this.showHotSearch = true;
},
// 搜索历史项点击
searchHistoryItem(keyword) {
this.placeInput = keyword;
this.getLocations(keyword);
},
// 热门标签点击
searchHotTag(tag) {
this.placeInput = tag;
this.getLocations(tag);
this.saveToSearchHistory(tag);
},
// 保存到搜索历史
saveToSearchHistory(keyword) {
if (!keyword || keyword.trim() === '') return;
try {
// 去重
const index = this.searchHistory.indexOf(keyword);
if (index > -1) {
this.searchHistory.splice(index, 1);
}
// 添加到开头
this.searchHistory.unshift(keyword);
// 限制历史记录数量
if (this.searchHistory.length > 10) {
this.searchHistory.pop();
}
// 保存到本地存储
uni.setStorageSync('placeSearchHistory', this.searchHistory);
} catch (e) {
console.error('保存搜索历史失败:', e);
// 即使保存失败也不影响功能使用
}
},
// 加载搜索历史
loadSearchHistory() {
try {
const history = uni.getStorageSync('placeSearchHistory');
if (history && Array.isArray(history)) {
this.searchHistory = history;
}
} catch (e) {
console.error('加载搜索历史失败:', e);
// 即使加载失败也不影响功能使用
}
},
// 搜索地点
getLocations(place) {
if (!place || place.trim() === '') {
this.placeList = [];
return;
}
// 显示搜索中状态
this.isSearching = true;
// this.showHistory = false;
this.showHotSearch = false;
// 模拟搜索请求实际项目中使用真实API
// 这里添加了模拟数据确保在无网络或API限制时也能看到搜索效果
setTimeout(() => {
// 模拟搜索结果数据
const mockResults = [
{
name: `${place}购物中心`,
address: `山东省济南市历下区${place}路123号`,
location: { lat: 36.657017, lng: 117.123237 }
},
{
name: `${place}公园`,
address: `山东省济南市槐荫区${place}路456号`,
location: { lat: 36.667017, lng: 117.133237 }
},
{
name: `${place}小区`,
address: `山东省济南市历城区${place}路789号`,
location: { lat: 36.677017, lng: 117.143237 }
}
];
this.placeList = mockResults;
this.isSearching = false;
// 保存到搜索历史
this.saveToSearchHistory(place);
// 实际项目中使用的真实API请求
/*
uni.request({
url: 'https://api.map.baidu.com/place/v2/suggestion',
data: {
q: place,
ak: "qr93Dm5Ph6Vb4n1aTfvHG9KZkvG8S4YU",
region: '济南市',
output: "json",
},
success: (res) => {
console.log('地点搜索结果:', res.data);
if (res.data && res.data.result) {
this.placeList = res.data.result;
}
},
fail: (err) => {
console.log('搜索地点失败:', err);
uni.showToast({
title: '搜索失败,请重试',
icon: 'none'
});
},
complete: () => {
this.isSearching = false;
// 保存到搜索历史
this.saveToSearchHistory(place);
}
});
*/
}, 800); // 模拟网络延迟
},
// 选择地点
addIcon(placeName) {
const selectedPlace = this.placeList.find(item => item.name === placeName);
if (selectedPlace) {
this.checkedMarker = selectedPlace;
// 更新地图中心点和标记
this.mapCenter = {
latitude: selectedPlace.location.lat,
longitude: selectedPlace.location.lng
};
// 添加标记点
this.markers = [{
id: 1,
latitude: selectedPlace.location.lat,
longitude: selectedPlace.location.lng,
title: selectedPlace.name,
width: 32,
height: 32
}];
// 显示选择结果
uni.showToast({
title: `已选择: ${selectedPlace.name}`,
icon: 'none',
duration: 2000
});
this.placeList = [];
}
},
// 标记点点击事件
onMarkerTap(e) {
const markerId = e.markerId;
const marker = this.markers.find(m => m.id === markerId);
if (marker) {
this.checkedMarker = {
name: marker.title,
location: {
lat: marker.latitude,
lng: marker.longitude
}
};
}
},
// 地图区域变化事件
onRegionChange(e) {
if (e.type === 'end' && e.detail.centerLocation) {
// 可以在这里处理地图拖动后的逻辑
console.log('地图中心变化:', e.detail.centerLocation);
}
},
// 点击地图事件
onMapTap(e) {
console.log('点击地图:', e);
const { latitude, longitude } = e.detail;
// 通过坐标获取地点信息
this.getLocationInfo(latitude, longitude);
},
// 根据坐标获取地点信息
getLocationInfo(latitude, longitude) {
// 模拟请求
setTimeout(() => {
const locationInfo = {
formatted_address: `点击位置(${latitude.toFixed(6)},${longitude.toFixed(6)})`,
addressComponent: {
district: '历下区',
street: '经十路',
street_number: '123号'
}
};
// 更新选中的地点信息
this.checkedMarker = {
name: locationInfo.formatted_address,
address: `${locationInfo.addressComponent.district}${locationInfo.addressComponent.street}${locationInfo.addressComponent.street_number}`,
location: {
lat: latitude,
lng: longitude
}
};
// 更新地图标记
this.markers = [{
id: 1,
latitude: latitude,
longitude: longitude,
title: this.checkedMarker.name,
width: 32,
height: 32
}];
// 显示选择结果
uni.showToast({
title: `已选择: ${this.checkedMarker.name}`,
icon: 'none',
duration: 2000
});
}, 500);
/* 实际项目中使用的真实API请求
uni.request({
url: 'https://api.map.baidu.com/reverse_geocoding/v3',
data: {
ak: "qr93Dm5Ph6Vb4n1aTfvHG9KZkvG8S4YU",
output: "json",
coordtype: "gcj02ll",
location: `${latitude},${longitude}`
},
success: (res) => {
console.log('逆地理编码结果:', res.data);
if (res.data && res.data.result) {
const locationInfo = res.data.result;
// 更新选中的地点信息
this.checkedMarker = {
name: locationInfo.formatted_address || '未知地点',
address: locationInfo.formatted_address,
location: {
lat: latitude,
lng: longitude
}
};
// 更新地图标记
this.markers = [{
id: 1,
latitude: latitude,
longitude: longitude,
title: this.checkedMarker.name,
width: 32,
height: 32
}];
// 显示选择结果
uni.showToast({
title: `已选择: ${this.checkedMarker.name}`,
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
console.log('获取地点信息失败:', err);
uni.showToast({
title: '获取地点信息失败',
icon: 'none'
});
}
});
*/
}
}
}
</script>
<style lang="scss" scoped>
.search-container {
position: relative;
z-index: 2;
padding: 24rpx 32rpx;
background: #ffffff;
border-radius: 0 0 16rpx 16rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
max-height: 50vh;
overflow-y: auto;
}
/* 搜索区域样式 */
.search-area {
width: 100%;
background: #fff;
box-sizing: border-box;
border-radius: 12rpx;
}
/* 搜索输入框包装 */
.search-input-wrapper {
position: relative;
margin-bottom: 16rpx;
border: 2rpx solid #E5E5E5;
border-radius: 8rpx;
background: #F5F7FA;
transition: all 0.3s ease;
&:focus-within {
border-color: #1D64CF;
background: #ffffff;
box-shadow: 0 0 0 4rpx rgba(29, 100, 207, 0.1);
}
}
/* 搜索图标 */
.search-icon {
position: absolute;
left: 24rpx;
top: 50%;
transform: translateY(-50%);
z-index: 1;
}
/* 输入框样式 */
.search-input-wrapper .u-input {
padding-left: 72rpx !important;
padding-right: 80rpx !important;
background: transparent !important;
border: none !important;
border-radius: 8rpx !important;
font-size: 28rpx;
color: #333333;
}
/* 搜索加载动画 */
.search-loading {
position: absolute;
right: 24rpx;
top: 50%;
transform: translateY(-50%);
}
/* 搜索历史样式 */
.search-history {
margin-top: 20rpx;
padding: 16rpx 0;
}
.history-title {
font-size: 26rpx;
color: #999999;
margin-bottom: 16rpx;
}
.history-list {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.history-item {
display: flex;
align-items: center;
padding: 16rpx 20rpx;
background: #F5F7FA;
border-radius: 8rpx;
font-size: 26rpx;
color: #333333;
transition: background-color 0.2s ease;
&:active {
background: #E5E9F2;
}
}
.history-icon {
margin-right: 12rpx;
}
.empty-history {
text-align: center;
color: #999999;
font-size: 24rpx;
padding: 40rpx 0;
}
/* 热门搜索样式 */
.hot-search {
margin-top: 20rpx;
padding: 16rpx 0;
}
.hot-title {
font-size: 26rpx;
color: #999999;
margin-bottom: 16rpx;
}
.hot-tags {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.hot-tag {
padding: 12rpx 24rpx;
background: #F5F7FA;
border-radius: 20rpx;
font-size: 26rpx;
color: #333333;
transition: all 0.2s ease;
&:active {
background: #1D64CF;
color: #ffffff;
}
}
/* 选中项样式优化 */
.selected {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 24rpx;
background: #DCE2E9;
border-radius: 8rpx;
margin-bottom: 16rpx;
animation: fadeIn 0.3s ease;
}
.selected-content {
display: flex;
align-items: center;
flex: 1;
}
.selected-text {
font-size: 26rpx;
color: #333333;
margin-left: 12rpx;
}
.selected-address {
font-size: 24rpx;
color: #666666;
}
.clear-btn {
color: #ff4757;
font-size: 24rpx;
padding: 8rpx 16rpx;
background: rgba(255, 71, 87, 0.1);
border-radius: 4rpx;
transition: all 0.2s ease;
&:active {
background: rgba(255, 71, 87, 0.2);
}
}
/* 搜索结果样式 */
.search-results {
margin-top: 16rpx;
}
.results-title {
font-size: 26rpx;
color: #999999;
margin-bottom: 16rpx;
}
.place-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #ffffff;
border-radius: 8rpx;
margin-bottom: 12rpx;
border: 2rpx solid #F5F7FA;
transition: all 0.2s ease;
&:active {
background: #F5F7FA;
border-color: #1D64CF;
}
}
.item-icon {
margin-right: 20rpx;
flex-shrink: 0;
}
.item-content {
flex: 1;
min-width: 0;
}
.item-name {
font-size: 28rpx;
color: #333333;
margin-bottom: 4rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-address {
font-size: 24rpx;
color: #999999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-arrow {
margin-left: 16rpx;
flex-shrink: 0;
}
/* 无结果样式 */
.no-results {
text-align: center;
padding: 60rpx 20rpx;
color: #999999;
}
.no-results-tip {
display: block;
font-size: 24rpx;
margin-top: 12rpx;
color: #CCCCCC;
}
/* 地图样式 */
.map {
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
left: 0;
top: 0;
}
/* 底部按钮区域样式 */
.button-area {
position: absolute;
z-index: 2;
bottom: 0;
right:0;
padding: 24rpx 32rpx;
width: 100%;
background: #fff;
display: flex;
box-sizing: border-box;
margin-top: 40rpx;
border-radius: 16px 16px 0px 0px;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
.btn {
line-height: 72rpx;
width: 176rpx;
margin-right: 16rpx;
font-size: 28rpx;
border: 1px solid #B8C5D4;
color: #282828;
text-align: center;
border-radius: 8rpx;
transition: all 0.2s ease;
&:active {
opacity: 0.8;
}
}
.reset {
background: #DCE2E9;
}
.save {
background: linear-gradient(103deg, #1D64CF 0%, #1590D4 99%);
color: #fff;
border: 0;
flex-grow: 1;
&:active {
opacity: 0.9;
transform: translateY(1rpx);
}
}
}
/* 动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>