This commit is contained in:
aaron 2025-03-16 15:00:00 +08:00
parent 018bc7c640
commit 132eb9b063

View File

@ -2,60 +2,62 @@
<div class="map-picker-container">
<a-form-item :label="label">
<div class="region-search-container">
<!-- 省市选择器 -->
<div class="region-selector">
<a-select
v-model:value="selectedProvince"
placeholder="选择省份"
style="width: 48%"
@change="handleProvinceChange"
:options="provinceOptions"
show-search
:filter-option="filterOption"
/>
<a-select
v-model:value="selectedCity"
placeholder="选择城市"
style="width: 48%"
@change="handleCityChange"
:options="cityOptions"
:disabled="!selectedProvince"
show-search
:filter-option="filterOption"
/>
<div class="search-input-group">
<a-input
v-model:value="searchAddress"
class="map-picker-input"
placeholder="请输入详细地址"
:loading="searchLoading"
@change="handleAddressChange"
>
<template #suffix>
<search-outlined />
</template>
</a-input>
<a-button type="primary" @click="handleSearch(searchAddress)" :loading="searchLoading">
搜索
</a-button>
</div>
<!-- 详细地址搜索 -->
<a-auto-complete
v-model:value="searchAddress"
:options="searchOptions"
placeholder="输入详细地址搜索"
@change="handleSearch"
@select="handleSelect"
:loading="searchLoading"
allow-clear
class="map-picker-input"
<!-- 搜索结果列表 -->
<div v-if="searchOptions.length > 0" class="search-results">
<div class="search-results-list">
<div
v-for="(option, index) in searchOptions"
:key="index"
class="search-result-item"
@click="handleSelect(option.value, option)"
>
{{ option.label }}
</div>
</div>
</div>
</div>
<div class="search-tip">地址搜索仅作为辅助填写小区地址的工具</div>
<div :id="mapContainerId" class="map-container" style="height: 300px;"></div>
<div class="address-display">
<div class="address-label">
<span class="required-mark">*</span>小区地址
</div>
<a-input
v-model:value="address"
placeholder="请选择小区地址"
readonly
class="address-input"
/>
</div>
</a-form-item>
<a-form-item label="地图选点" required>
<div class="map-container">
<div :id="mapContainerId" style="height: 300px;"></div>
<div class="form-item-tip">
<div>提示: 可直接在地图上点击选择位置或搜索地址后选择</div>
</div>
<div class="form-item-tip">在地图上点击选择准确位置或通过上方搜索框搜索地址</div>
<!-- 隐藏的输入框用于表单验证 -->
<a-input v-model:value="longitude" style="display: none;" />
<a-input v-model:value="latitude" style="display: none;" />
</a-form-item>
<a-form-item label="详细地址" required>
<a-input v-model:value="address" placeholder="请输入详细地址" />
<div class="form-item-tip">地址将用于配送员导航和定位请确保准确</div>
</a-form-item>
<!-- 隐藏经纬度输入字段但保持功能不变 -->
<div style="display: none;">
<a-input-number v-model:value="longitude" :min="-180" :max="180" disabled />
<a-input-number v-model:value="latitude" :min="-90" :max="90" disabled />
</div>
</div>
</template>
@ -193,73 +195,11 @@ export default {
const searchOptions = ref([])
const searchLoading = ref(false)
//
const provinceOptions = ref(fallbackProvinces)
const cityOptions = ref([])
const selectedProvince = ref('')
const selectedCity = ref('')
//
const address = ref(props.modelValue.address)
const longitude = ref(props.modelValue.longitude)
const latitude = ref(props.modelValue.latitude)
//
const filterOption = (input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
//
const handleProvinceChange = (value) => {
selectedProvince.value = value
selectedCity.value = ''
//
cityOptions.value = fallbackCitiesMap[value] || []
//
if (map.value) {
try {
// 使
const geocoder = createGeocoder()
geocoder.getLocation(value, (status, result) => {
if (status === 'complete' && result.info === 'OK' && result.geocodes.length > 0) {
const location = result.geocodes[0].location
map.value.setCenter([location.lng, location.lat])
map.value.setZoom(8) // 8
}
})
} catch (error) {
console.error('设置地图中心点失败:', error)
}
}
}
//
const handleCityChange = (value) => {
selectedCity.value = value
//
if (map.value) {
try {
// 使
const geocoder = createGeocoder()
geocoder.getLocation(value, (status, result) => {
if (status === 'complete' && result.info === 'OK' && result.geocodes.length > 0) {
const location = result.geocodes[0].location
map.value.setCenter([location.lng, location.lat])
map.value.setZoom(12) // 12
//
searchAddress.value = ''
}
})
} catch (error) {
console.error('设置地图中心点失败:', error)
}
}
}
// props
watch(() => props.modelValue, (newVal, oldVal) => {
console.log('props变化:', newVal, oldVal)
@ -302,24 +242,13 @@ export default {
const city = addressComponent.city
//
const foundProvince = provinceOptions.value.find(p =>
const foundProvince = fallbackProvinces.find(p =>
province.includes(p.value) || p.value.includes(province)
)
if (foundProvince) {
selectedProvince.value = foundProvince.value
//
cityOptions.value = fallbackCitiesMap[foundProvince.value] || []
if (city) {
const foundCity = cityOptions.value.find(c =>
city.includes(c.value) || c.value.includes(city)
)
if (foundCity) {
selectedCity.value = foundCity.value
}
}
longitude.value = newVal.longitude
latitude.value = newVal.latitude
}
}
}
@ -349,7 +278,8 @@ export default {
emit('update:modelValue', {
address: address.value,
longitude: longitude.value,
latitude: latitude.value
latitude: latitude.value,
isValid: !!address.value && !!longitude.value && !!latitude.value
})
}
}
@ -410,34 +340,21 @@ export default {
const city = addressComponent.city
//
const foundProvince = provinceOptions.value.find(p =>
const foundProvince = fallbackProvinces.find(p =>
province.includes(p.value) || p.value.includes(province)
)
if (foundProvince) {
selectedProvince.value = foundProvince.value
//
cityOptions.value = fallbackCitiesMap[foundProvince.value] || []
if (city) {
const foundCity = cityOptions.value.find(c =>
city.includes(c.value) || c.value.includes(city)
)
if (foundCity) {
selectedCity.value = foundCity.value
}
}
longitude.value = lng
latitude.value = lat
}
}
updateValue()
} else {
// 使
if (selectedCity.value) {
address.value = `${selectedProvince.value} ${selectedCity.value} (${lng.toFixed(6)},${lat.toFixed(6)})`
} else if (selectedProvince.value) {
address.value = `${selectedProvince.value} (${lng.toFixed(6)},${lat.toFixed(6)})`
if (latitude.value) {
address.value = `${fallbackProvinces.find(p => p.value === latitude.value.split(' ')[0]).label} (${lng.toFixed(6)},${lat.toFixed(6)})`
} else {
address.value = `位置坐标: ${lng.toFixed(6)},${lat.toFixed(6)}`
}
@ -498,25 +415,18 @@ export default {
}
const autoComplete = createAutoComplete({
city: selectedCity.value || selectedProvince.value || '全国',
citylimit: !!selectedCity.value
city: '全国',
citylimit: false
})
//
let keyword = value
if (selectedCity.value) {
keyword = `${selectedCity.value} ${value}`
} else if (selectedProvince.value) {
keyword = `${selectedProvince.value} ${value}`
}
autoComplete.search(keyword, (status, result) => {
autoComplete.search(value, (status, result) => {
if (status === 'complete' && result.tips && result.tips.length > 0) {
searchOptions.value = result.tips.map(tip => ({
value: tip.name,
label: `${tip.name} (${tip.district || ''})`,
location: tip.location
}))
console.log('搜索结果:', searchOptions.value)
} else {
//
searchOptions.value = [{
@ -542,22 +452,36 @@ export default {
}
}
//
const handleAddressChange = (e) => {
//
const value = e.target.value
//
searchAddress.value = value
//
if (!value) {
searchOptions.value = []
}
}
//
const handleSelect = (value, option) => {
//
searchOptions.value = []
// 使
address.value = value
//
const selected = searchOptions.value.find(opt => opt.value === value)
//
if (!selected || selected.manualInput || !selected.location) {
if (!option || option.manualInput || !option.location) {
message.info('请在地图上选择具体位置')
updateValue()
return
}
const { lng, lat } = selected.location
const { lng, lat } = option.location
//
updateMarkerPosition(lng, lat)
@ -582,24 +506,13 @@ export default {
const city = addressComponent.city
//
const foundProvince = provinceOptions.value.find(p =>
const foundProvince = fallbackProvinces.find(p =>
province.includes(p.value) || p.value.includes(province)
)
if (foundProvince) {
selectedProvince.value = foundProvince.value
//
cityOptions.value = fallbackCitiesMap[foundProvince.value] || []
if (city) {
const foundCity = cityOptions.value.find(c =>
city.includes(c.value) || c.value.includes(city)
)
if (foundCity) {
selectedCity.value = foundCity.value
}
}
longitude.value = lng
latitude.value = lat
}
}
}
@ -630,14 +543,7 @@ export default {
latitude,
handleSearch,
handleSelect,
//
provinceOptions,
cityOptions,
selectedProvince,
selectedCity,
handleProvinceChange,
handleCityChange,
filterOption
handleAddressChange
}
}
}
@ -653,18 +559,69 @@ export default {
flex-direction: column;
gap: 12px;
margin-bottom: 8px;
position: relative;
}
.region-selector {
.search-input-group {
display: flex;
justify-content: space-between;
gap: 8px;
align-items: stretch;
height: 32px;
position: relative;
z-index: 1;
}
.map-picker-input {
flex: 1;
background: transparent;
}
.search-tip {
font-size: 12px;
color: #999;
margin-top: 4px;
margin-bottom: 8px;
font-style: italic;
}
.search-results {
position: absolute;
top: 40px;
left: 0;
right: 0;
z-index: 1001;
background: white;
max-height: 300px;
overflow-y: auto;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
border-radius: 4px;
border: 1px solid #d9d9d9;
}
.search-results-list {
display: flex;
flex-direction: column;
width: 100%;
}
.search-result-item {
cursor: pointer;
padding: 10px 12px;
transition: background-color 0.3s;
border-bottom: 1px solid #f0f0f0;
line-height: 1.5;
width: 100%;
text-align: left;
}
.search-result-item:last-child {
border-bottom: none;
}
.search-result-item:hover {
background-color: #f5f5f5;
}
.map-container {
border: 1px solid #d9d9d9;
border-radius: 4px;
@ -674,6 +631,37 @@ export default {
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.address-display {
margin-top: 8px;
}
.address-label {
margin-bottom: 4px;
font-size: 14px;
color: rgba(0, 0, 0, 0.85);
}
.required-mark {
color: #ff4d4f;
margin-right: 4px;
}
.address-input {
width: 100%;
}
:deep(.address-input .ant-input) {
background-color: #f5f5f5;
cursor: default;
color: #333;
font-weight: 500;
}
:deep(.address-input .ant-input:hover),
:deep(.address-input .ant-input:focus) {
background-color: #f0f0f0;
}
.form-item-tip {
margin-top: 8px;
font-size: 0.8em;
@ -693,7 +681,38 @@ export default {
padding: 6px 12px;
}
:deep(.ant-input-affix-wrapper),
:deep(.ant-btn) {
height: 32px;
}
:deep(.ant-input-affix-wrapper) {
border-radius: 4px;
display: flex;
align-items: center;
background: white;
border: 1px solid #d9d9d9;
overflow: hidden;
}
:deep(.ant-input-affix-wrapper:hover) {
border-color: #40a9ff;
}
:deep(.ant-input-affix-wrapper-focused) {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
:deep(.ant-btn) {
display: flex;
align-items: center;
justify-content: center;
padding: 0 15px;
}
:deep(.ant-input) {
height: 100%;
background: transparent;
}
</style>