You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

453 lines
9.8 KiB

<template>
<view class="container">
<!-- 搜索栏 -->
<view class="search-header">
<view class="search-box">
<uv-icon name="search" color="#999" size="16"></uv-icon>
<input
class="search-input"
v-model="searchKeyword"
placeholder="请输入地址"
confirm-type="search"
focus
@input="onSearchInput"
@confirm="onSearchConfirm"
/>
<uv-icon
v-if="searchKeyword"
name="close"
color="#999"
size="16"
@click="clearSearch"
></uv-icon>
</view>
<view class="cancel-btn" @click="goBack">取消</view>
</view>
<!-- 搜索历史 -->
<view class="section" v-if="!searchKeyword && searchHistory.length > 0">
<view class="section-header">
<text class="section-title">搜索历史</text>
<uv-icon
name="trash"
color="#999"
size="16"
@click="clearHistory"
></uv-icon>
</view>
<view class="history-list">
<view
class="history-item"
v-for="(item, index) in searchHistory"
:key="index"
@click="useHistoryItem(item)"
>
<uv-icon name="time" color="#999" size="16"></uv-icon>
<text class="history-text">{{ item }}</text>
</view>
</view>
</view>
<!-- 搜索结果 -->
<view class="search-results" v-if="searchKeyword">
<view class="loading-box" v-if="isSearching">
<uv-loading-icon></uv-loading-icon>
<text>搜索中...</text>
</view>
<view
v-else-if="searchResults.length === 0 && searchKeyword"
class="no-result"
>
<text>没有找到相关地址</text>
</view>
<view v-else class="result-list">
<view
class="result-item"
v-for="(item, index) in searchResults"
:key="index"
@click="selectAddress(item)"
>
<view class="result-icon">
<uv-icon name="map-fill" color="#4080FF" size="20"></uv-icon>
</view>
<view class="result-info">
<view class="result-name">{{ item.name }}</view>
<view class="result-address">{{ item.address }}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, watch,onMounted } from "vue";
import { useUserStore } from "@/store";
// 搜索关键词
const searchKeyword = ref("");
// 搜索历史
const searchHistory = ref([]);
// 搜索结果
const searchResults = ref([]);
// 是否正在搜索
const isSearching = ref(false);
// 从本地存储加载搜索历史
const loadSearchHistory = () => {
try {
const history = uni.getStorageSync("searchAddressHistory");
if (history) {
searchHistory.value = JSON.parse(history);
}
} catch (e) {
console.error("加载搜索历史失败", e);
}
};
// 保存搜索历史到本地存储
const saveSearchHistory = () => {
try {
uni.setStorageSync(
"searchAddressHistory",
JSON.stringify(searchHistory.value)
);
} catch (e) {
console.error("保存搜索历史失败", e);
}
};
// 添加搜索历史
const addToHistory = (keyword) => {
// 如果已存在,先移除
const index = searchHistory.value.indexOf(keyword);
if (index !== -1) {
searchHistory.value.splice(index, 1);
}
// 添加到最前面
searchHistory.value.unshift(keyword);
// 限制历史记录数量
if (searchHistory.value.length > 10) {
searchHistory.value.pop();
}
// 保存到本地存储
saveSearchHistory();
};
// 清空搜索历史
const clearHistory = () => {
uni.showModal({
title: "提示",
content: "确定要清空搜索历史吗?",
success: (res) => {
if (res.confirm) {
searchHistory.value = [];
saveSearchHistory();
}
},
});
};
// 使用历史记录项
const useHistoryItem = (keyword) => {
searchKeyword.value = keyword;
searchAddress(keyword);
};
// 清空搜索框
const clearSearch = () => {
searchKeyword.value = "";
searchResults.value = [];
};
// 搜索地址
const searchAddress = (keyword) => {
if (!keyword.trim()) {
searchResults.value = [];
return;
}
isSearching.value = true;
// 这里应该调用地图API进行地址搜索
// 示例使用模拟数据
setTimeout(() => {
// 模拟搜索结果
if (keyword.includes("上海") || keyword.includes("天河")) {
searchResults.value = [
{
name: "通力电梯(上海旗舰店)",
address: "上海天河区珠江城洋村路11号",
latitude: 31.2304,
longitude: 121.4737,
},
{
name: "上海中心大厦",
address: "上海市浦东新区陆家嘴环路479号",
latitude: 31.2338,
longitude: 121.5079,
},
{
name: "上海天河城",
address: "上海市徐汇区天钥桥路123号",
latitude: 31.1898,
longitude: 121.4326,
},
];
} else if (keyword.includes("北京")) {
searchResults.value = [
{
name: "北京通力电梯有限公司",
address: "北京市朝阳区建国路88号",
latitude: 39.9042,
longitude: 116.4074,
},
{
name: "北京国贸中心",
address: "北京市朝阳区建国门外大街1号",
latitude: 39.9088,
longitude: 116.4614,
},
];
} else {
searchResults.value = [];
}
isSearching.value = false;
// 添加到搜索历史
if (searchResults.value.length > 0) {
addToHistory(keyword);
}
}, 800);
// 实际应用中应该使用类似下面的代码调用地图API
/*
uni.request({
url: 'https://apis.map.qq.com/ws/place/v1/search',
method: 'GET',
data: {
keyword: keyword,
boundary: 'region(上海,0)', // 可以根据当前城市动态设置
key: 'YOUR_MAP_KEY'
},
success: (res) => {
isSearching.value = false;
if (res.data.status === 0 && res.data.data.length > 0) {
searchResults.value = res.data.data.map(item => ({
name: item.title,
address: item.address,
latitude: item.location.lat,
longitude: item.location.lng
}));
// 添加到搜索历史
addToHistory(keyword);
} else {
searchResults.value = [];
}
},
fail: (err) => {
isSearching.value = false;
console.error('搜索地址失败', err);
uni.showToast({
title: '搜索失败,请重试',
icon: 'none'
});
}
});
*/
};
// 监听搜索输入
const onSearchInput = () => {
if (searchKeyword.value) {
// 可以实现输入时实时搜索
// 这里为了性能考虑,不实时搜索,而是在用户停止输入一段时间后搜索
clearTimeout(window.searchTimeout);
window.searchTimeout = setTimeout(() => {
searchAddress(searchKeyword.value);
}, 500);
} else {
searchResults.value = [];
}
};
// 搜索确认
const onSearchConfirm = () => {
if (searchKeyword.value.trim()) {
searchAddress(searchKeyword.value);
}
};
// 选择地址
const selectAddress = (address) => {
// 将选择的地址保存到store
useUserStore().setAdressInfo({
latitude: address.latitude,
longitude: address.longitude,
adress: address.name,
addressDetail: address.address,
});
// 返回上一页
uni.navigateBack({
delta: 1,
});
};
// 返回上一页
const goBack = () => {
uni.navigateBack({
delta: 1,
});
};
// 页面加载时
onMounted(() => {
loadSearchHistory();
});
</script>
<style lang="scss" scoped>
.container {
font-size: 14px;
min-height: 100vh;
background-color: #f8f8f8;
}
.search-header {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #ffffff;
.search-box {
flex: 1;
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 30rpx;
padding: 10rpx 20rpx;
.search-input {
flex: 1;
height: 60rpx;
margin: 0 20rpx;
font-size: 28rpx;
}
}
.cancel-btn {
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
}
}
.section {
background-color: #ffffff;
margin-top: 20rpx;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1px solid #f5f5f5;
.section-title {
font-size: 28rpx;
color: #333333;
}
}
.history-list {
padding: 0 30rpx;
.history-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1px solid #f5f5f5;
.history-text {
margin-left: 20rpx;
font-size: 28rpx;
color: #333333;
}
&:last-child {
border-bottom: none;
}
}
}
}
.search-results {
background-color: #ffffff;
margin-top: 20rpx;
min-height: 200rpx;
.loading-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40rpx 0;
text {
margin-top: 20rpx;
font-size: 28rpx;
color: #999999;
}
}
.no-result {
display: flex;
justify-content: center;
align-items: center;
padding: 60rpx 0;
color: #999999;
font-size: 28rpx;
}
.result-list {
.result-item {
display: flex;
padding: 20rpx 30rpx;
border-bottom: 1px solid #f5f5f5;
.result-icon {
margin-right: 20rpx;
display: flex;
align-items: center;
}
.result-info {
flex: 1;
.result-name {
font-size: 30rpx;
color: #333333;
margin-bottom: 10rpx;
}
.result-address {
font-size: 26rpx;
color: #999999;
}
}
&:last-child {
border-bottom: none;
}
}
}
}
</style>