Files
ks-app-employment-service/packageRc/components/placePicker.vue

884 lines
21 KiB
Vue
Raw Normal View History

2025-11-03 12:30:37 +08:00
<template>
<view>
2025-11-04 15:16:22 +08:00
<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> -->
2025-11-03 12:30:37 +08:00
</view>
2025-11-04 15:16:22 +08:00
<!-- 搜索历史记录 -->
<!-- <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>
<!-- 无结果提示 -->
2025-11-03 12:30:37 +08:00
</view>
</view>
2025-11-04 15:16:22 +08:00
<!-- 地图组件 -->
<map
id="map"
class="map"
:latitude="mapCenter.latitude"
:longitude="mapCenter.longitude"
:markers="markers"
:scale="15"
show-location
@markertap="onMarkerTap"
@regionchange="onRegionChange"
@tap="onMapTap"
></map>
<!-- 底部按钮 -->
2025-11-03 12:30:37 +08:00
<view class="button-area">
2025-11-04 15:16:22 +08:00
<view class="btn" @tap="cancel"> </view>
<view class="btn save" @tap="submitForm"> </view>
2025-11-03 12:30:37 +08:00
</view>
</view>
2025-11-04 15:16:22 +08:00
</uni-popup>
2025-11-03 12:30:37 +08:00
</view>
</template>
<script>
2025-11-04 15:16:22 +08:00
// 请修改saveToSearchHistory和loadSearchHistory方法添加异常处理
2025-11-03 12:30:37 +08:00
export default {
data() {
return {
placeList: [],
placeInput: '',
checkedMarker: '',
visible: false,
2025-11-04 15:16:22 +08:00
// 地图中心点(济南市)
mapCenter: {
latitude: 36.657017,
longitude: 117.123237
},
// 地图标记点
markers: [],
// 当前选中的标记点
currentMarker: null,
// 搜索状态
isSearching: false,
showHistory: true,
showHotSearch: true,
// 搜索历史
searchHistory: [],
// 热门搜索标签
hotSearchTags: ['济南西站', '泉城广场', '大明湖', '千佛山', '济南东站', '融创文旅城']
2025-11-03 12:30:37 +08:00
}
},
mounted() {
2025-11-04 15:16:22 +08:00
// 获取用户位置权限
this.getUserLocation();
// 加载搜索历史
this.loadSearchHistory();
2025-11-03 12:30:37 +08:00
},
methods: {
2025-11-04 15:16:22 +08:00
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);
// 使用默认位置(济南市)
}
});
},
2025-11-03 12:30:37 +08:00
submitForm() {
2025-11-04 15:16:22 +08:00
if(this.checkedMarker && this.checkedMarker.name){
2025-11-03 12:30:37 +08:00
this.$emit('selected', this.checkedMarker)
2025-11-04 15:16:22 +08:00
// this.visible = false;
this.$refs.visible.close();
2025-11-03 12:30:37 +08:00
}else{
2025-11-04 15:16:22 +08:00
uni.showToast({
title: '您尚未选择地点!',
icon: 'none'
});
2025-11-03 12:30:37 +08:00
}
},
2025-11-04 15:16:22 +08:00
2025-11-03 12:30:37 +08:00
cancel() {
2025-11-04 15:16:22 +08:00
this.$refs.visible.close();
// this.visible = false;
2025-11-03 12:30:37 +08:00
},
2025-11-04 15:16:22 +08:00
2025-11-03 12:30:37 +08:00
openDialog() {
2025-11-04 15:16:22 +08:00
this.$refs.visible.open();
// this.visible = true;
this.placeInput = '';
this.placeList = [];
this.checkedMarker = '';
this.markers = [];
this.isSearching = false;
// this.showHistory = true;
this.showHotSearch = true;
2025-11-03 12:30:37 +08:00
},
2025-11-04 15:16:22 +08:00
// 重置选择
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);
2025-11-03 12:30:37 +08:00
}
2025-11-04 15:16:22 +08:00
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;
2025-11-03 12:30:37 +08:00
}
2025-11-04 15:16:22 +08:00
} 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);
}
2025-11-03 12:30:37 +08:00
});
2025-11-04 15:16:22 +08:00
*/
}, 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);
}
2025-11-03 12:30:37 +08:00
},
2025-11-04 15:16:22 +08:00
// 点击地图事件
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'
});
}
});
*/
2025-11-03 12:30:37 +08:00
}
}
}
</script>
<style lang="scss" scoped>
2025-11-04 15:16:22 +08:00
.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;
}
}
/* 选中项样式优化 */
2025-11-03 12:30:37 +08:00
.selected {
2025-11-04 15:16:22 +08:00
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 24rpx;
2025-11-03 12:30:37 +08:00
background: #DCE2E9;
border-radius: 8rpx;
2025-11-04 15:16:22 +08:00
margin-bottom: 16rpx;
animation: fadeIn 0.3s ease;
2025-11-03 12:30:37 +08:00
}
2025-11-04 15:16:22 +08:00
.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 {
2025-11-03 12:30:37 +08:00
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
left: 0;
top: 0;
}
2025-11-04 15:16:22 +08:00
/* 底部按钮区域样式 */
.button-area {
2025-11-03 12:30:37 +08:00
position: absolute;
z-index: 2;
bottom: 0;
2025-11-04 15:16:22 +08:00
right:0;
padding: 24rpx 32rpx;
2025-11-03 12:30:37 +08:00
width: 100%;
background: #fff;
display: flex;
box-sizing: border-box;
margin-top: 40rpx;
border-radius: 16px 16px 0px 0px;
2025-11-04 15:16:22 +08:00
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
.btn {
2025-11-03 12:30:37 +08:00
line-height: 72rpx;
width: 176rpx;
margin-right: 16rpx;
font-size: 28rpx;
border: 1px solid #B8C5D4;
color: #282828;
text-align: center;
border-radius: 8rpx;
2025-11-04 15:16:22 +08:00
transition: all 0.2s ease;
&:active {
opacity: 0.8;
}
2025-11-03 12:30:37 +08:00
}
2025-11-04 15:16:22 +08:00
.reset {
2025-11-03 12:30:37 +08:00
background: #DCE2E9;
}
2025-11-04 15:16:22 +08:00
.save {
2025-11-03 12:30:37 +08:00
background: linear-gradient(103deg, #1D64CF 0%, #1590D4 99%);
color: #fff;
border: 0;
flex-grow: 1;
2025-11-04 15:16:22 +08:00
&:active {
opacity: 0.9;
transform: translateY(1rpx);
}
2025-11-03 12:30:37 +08:00
}
}
2025-11-04 15:16:22 +08:00
/* 动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
2025-11-03 12:30:37 +08:00
}
</style>