This commit is contained in:
2026-07-30 15:05:56 +08:00
parent 1c5d50c339
commit f41ec31852
11 changed files with 269 additions and 49 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pointly-Tel - 高德POI商户查询</title>
<script type="module" crossorigin src="/assets/index-fNB4NbEb.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C6NseWna.css">
<script type="module" crossorigin src="/assets/index-BbrXGaLX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C3fajWoH.css">
</head>
<body>
<div id="app"></div>
+128 -7
View File
@@ -6,12 +6,34 @@
<!-- 顶部搜索栏 -->
<div class="top-bar">
<div class="search-form">
<input
v-model="keywords"
class="input"
placeholder="关键词(选填,如:火锅店)"
@keyup.enter="doSearch"
/>
<div class="input-wrap">
<input
v-model="keywords"
class="input"
placeholder="关键词(选填,如:火锅店)"
@input="onKeywordInput"
@compositionend="onKeywordInput"
@keydown.enter="doSearch(); hideSuggestions()"
@keydown.down.prevent="onSuggestArrow(1)"
@keydown.up.prevent="onSuggestArrow(-1)"
@keydown.esc="hideSuggestions"
@blur="onSuggestBlur"
autocomplete="off"
/>
</div>
<ul v-if="suggestions.length > 0" class="suggest-dropdown">
<li
v-for="(item, i) in suggestions"
:key="item.poiId || i"
class="suggest-item"
:class="{ active: i === suggestIndex }"
@mousedown.prevent="selectSuggestion(item)"
@mouseenter="suggestIndex = i"
>
<span class="suggest-name">{{ item.name }}</span>
<span v-if="item.city" class="suggest-city">{{ item.city }}</span>
</li>
</ul>
<select v-model="province" class="select province-select" @change="provinceChanged">
<option value="">所有省份</option>
<option v-for="p in provinces" :key="p" :value="p">{{ p }}</option>
@@ -87,7 +109,7 @@
<script setup>
import { ref, computed, watch } from 'vue'
import { searchPoi } from './api/poi.js'
import { searchPoi, suggestPoi } from './api/poi.js'
import PoiMap from './components/PoiMap.vue'
const mapRef = ref(null)
@@ -104,6 +126,9 @@ const error = ref('')
const page = ref(1)
const maxPage = ref(1)
const activePoi = ref(null)
const suggestions = ref([])
const suggestIndex = ref(-1)
let suggestTimer = null
const province = ref('')
const provinces = [
@@ -278,6 +303,46 @@ watch(radius, (val) => {
}
})
async function fetchSuggestions(kw) {
if (!kw.trim()) {
suggestions.value = []
return
}
try {
const data = await suggestPoi(kw, city.value)
suggestions.value = (data.tips || []).slice(0, 8)
suggestIndex.value = -1
} catch {
suggestions.value = []
}
}
function onKeywordInput() {
clearTimeout(suggestTimer)
suggestTimer = setTimeout(() => fetchSuggestions(keywords.value), 200)
}
function hideSuggestions() {
suggestions.value = []
suggestIndex.value = -1
}
function onSuggestBlur() {
setTimeout(hideSuggestions, 150)
}
function onSuggestArrow(dir) {
const len = suggestions.value.length
if (len === 0) return
suggestIndex.value = (suggestIndex.value + dir + len) % len
}
function selectSuggestion(item) {
keywords.value = item.name
hideSuggestions()
doSearch()
}
async function doSearch() {
error.value = ''
@@ -399,6 +464,7 @@ html, body, #app {
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
pointer-events: auto;
max-width: 700px;
position: relative;
}
.input {
@@ -415,6 +481,61 @@ html, body, #app {
border-color: #1a73e8;
}
.input-wrap {
position: relative;
flex: 1;
}
.input-wrap .input {
width: 100%;
}
.suggest-dropdown {
position: absolute;
top: 100%;
left: -16px;
right: -16px;
margin: 4px 0 0;
padding: 4px 0;
list-style: none;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 0 0 10px 10px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
z-index: 2000;
max-height: 320px;
overflow-y: auto;
}
.suggest-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 14px;
font-size: 13px;
cursor: pointer;
transition: background 0.1s;
}
.suggest-item.active,
.suggest-item:hover {
background: #f0f5ff;
}
.suggest-name {
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.suggest-city {
flex-shrink: 0;
margin-left: 8px;
font-size: 11px;
color: #999;
}
.select {
padding: 9px 10px;
border: 1px solid #ddd;
+7
View File
@@ -15,3 +15,10 @@ export async function searchPoi(keywords, city = '', page = 1, longitude = 0, la
const res = await api.get('/poi/search', { params })
return res.data.data
}
export async function suggestPoi(keywords, city = '') {
const params = { keywords }
if (city) params.city = city
const res = await api.get('/poi/suggest', { params })
return res.data.data
}
+11
View File
@@ -150,6 +150,17 @@ defineExpose({ addMarkers, clearMarkers, focusMarker, drawRadiusCircle, clearRad
min-height: 400px;
}
/* 缩放控件左侧垂直居中 */
:deep(.leaflet-control-zoom) {
margin-top: 0 !important;
}
:deep(.leaflet-top.leaflet-left) {
top: 50%;
transform: translateY(-50%);
position: absolute;
z-index: 1000;
}
:deep(.map-popup h4) {
margin: 0 0 4px;
font-size: 14px;