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"> <div class="map-picker-container">
<a-form-item :label="label"> <a-form-item :label="label">
<div class="region-search-container"> <div class="region-search-container">
<!-- 省市选择器 --> <div class="search-input-group">
<div class="region-selector"> <a-input
<a-select v-model:value="searchAddress"
v-model:value="selectedProvince" class="map-picker-input"
placeholder="选择省份" placeholder="请输入详细地址"
style="width: 48%" :loading="searchLoading"
@change="handleProvinceChange" @change="handleAddressChange"
:options="provinceOptions" >
show-search <template #suffix>
:filter-option="filterOption" <search-outlined />
/> </template>
<a-select </a-input>
v-model:value="selectedCity" <a-button type="primary" @click="handleSearch(searchAddress)" :loading="searchLoading">
placeholder="选择城市" 搜索
style="width: 48%" </a-button>
@change="handleCityChange"
:options="cityOptions"
:disabled="!selectedProvince"
show-search
:filter-option="filterOption"
/>
</div> </div>
<!-- 详细地址搜索 --> <!-- 搜索结果列表 -->
<a-auto-complete <div v-if="searchOptions.length > 0" class="search-results">
v-model:value="searchAddress" <div class="search-results-list">
:options="searchOptions" <div
placeholder="输入详细地址搜索" v-for="(option, index) in searchOptions"
@change="handleSearch" :key="index"
@select="handleSelect" class="search-result-item"
:loading="searchLoading" @click="handleSelect(option.value, option)"
allow-clear >
class="map-picker-input" {{ 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> </div>
</a-form-item>
<div class="form-item-tip">
<a-form-item label="地图选点" required> <div>提示: 可直接在地图上点击选择位置或搜索地址后选择</div>
<div class="map-container">
<div :id="mapContainerId" style="height: 300px;"></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>
<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> </div>
</template> </template>
@ -193,73 +195,11 @@ export default {
const searchOptions = ref([]) const searchOptions = ref([])
const searchLoading = ref(false) const searchLoading = ref(false)
//
const provinceOptions = ref(fallbackProvinces)
const cityOptions = ref([])
const selectedProvince = ref('')
const selectedCity = ref('')
// //
const address = ref(props.modelValue.address) const address = ref(props.modelValue.address)
const longitude = ref(props.modelValue.longitude) const longitude = ref(props.modelValue.longitude)
const latitude = ref(props.modelValue.latitude) 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 // props
watch(() => props.modelValue, (newVal, oldVal) => { watch(() => props.modelValue, (newVal, oldVal) => {
console.log('props变化:', newVal, oldVal) console.log('props变化:', newVal, oldVal)
@ -302,24 +242,13 @@ export default {
const city = addressComponent.city const city = addressComponent.city
// //
const foundProvince = provinceOptions.value.find(p => const foundProvince = fallbackProvinces.find(p =>
province.includes(p.value) || p.value.includes(province) province.includes(p.value) || p.value.includes(province)
) )
if (foundProvince) { if (foundProvince) {
selectedProvince.value = foundProvince.value longitude.value = newVal.longitude
latitude.value = newVal.latitude
//
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
}
}
} }
} }
} }
@ -349,7 +278,8 @@ export default {
emit('update:modelValue', { emit('update:modelValue', {
address: address.value, address: address.value,
longitude: longitude.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 city = addressComponent.city
// //
const foundProvince = provinceOptions.value.find(p => const foundProvince = fallbackProvinces.find(p =>
province.includes(p.value) || p.value.includes(province) province.includes(p.value) || p.value.includes(province)
) )
if (foundProvince) { if (foundProvince) {
selectedProvince.value = foundProvince.value longitude.value = lng
latitude.value = lat
//
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
}
}
} }
} }
updateValue() updateValue()
} else { } else {
// 使 // 使
if (selectedCity.value) { if (latitude.value) {
address.value = `${selectedProvince.value} ${selectedCity.value} (${lng.toFixed(6)},${lat.toFixed(6)})` address.value = `${fallbackProvinces.find(p => p.value === latitude.value.split(' ')[0]).label} (${lng.toFixed(6)},${lat.toFixed(6)})`
} else if (selectedProvince.value) {
address.value = `${selectedProvince.value} (${lng.toFixed(6)},${lat.toFixed(6)})`
} else { } else {
address.value = `位置坐标: ${lng.toFixed(6)},${lat.toFixed(6)}` address.value = `位置坐标: ${lng.toFixed(6)},${lat.toFixed(6)}`
} }
@ -498,25 +415,18 @@ export default {
} }
const autoComplete = createAutoComplete({ const autoComplete = createAutoComplete({
city: selectedCity.value || selectedProvince.value || '全国', city: '全国',
citylimit: !!selectedCity.value citylimit: false
}) })
// autoComplete.search(value, (status, result) => {
let keyword = value
if (selectedCity.value) {
keyword = `${selectedCity.value} ${value}`
} else if (selectedProvince.value) {
keyword = `${selectedProvince.value} ${value}`
}
autoComplete.search(keyword, (status, result) => {
if (status === 'complete' && result.tips && result.tips.length > 0) { if (status === 'complete' && result.tips && result.tips.length > 0) {
searchOptions.value = result.tips.map(tip => ({ searchOptions.value = result.tips.map(tip => ({
value: tip.name, value: tip.name,
label: `${tip.name} (${tip.district || ''})`, label: `${tip.name} (${tip.district || ''})`,
location: tip.location location: tip.location
})) }))
console.log('搜索结果:', searchOptions.value)
} else { } else {
// //
searchOptions.value = [{ 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) => { const handleSelect = (value, option) => {
//
searchOptions.value = []
// 使 // 使
address.value = 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('请在地图上选择具体位置') message.info('请在地图上选择具体位置')
updateValue() updateValue()
return return
} }
const { lng, lat } = selected.location const { lng, lat } = option.location
// //
updateMarkerPosition(lng, lat) updateMarkerPosition(lng, lat)
@ -582,24 +506,13 @@ export default {
const city = addressComponent.city const city = addressComponent.city
// //
const foundProvince = provinceOptions.value.find(p => const foundProvince = fallbackProvinces.find(p =>
province.includes(p.value) || p.value.includes(province) province.includes(p.value) || p.value.includes(province)
) )
if (foundProvince) { if (foundProvince) {
selectedProvince.value = foundProvince.value longitude.value = lng
latitude.value = lat
//
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
}
}
} }
} }
} }
@ -630,14 +543,7 @@ export default {
latitude, latitude,
handleSearch, handleSearch,
handleSelect, handleSelect,
// handleAddressChange
provinceOptions,
cityOptions,
selectedProvince,
selectedCity,
handleProvinceChange,
handleCityChange,
filterOption
} }
} }
} }
@ -653,18 +559,69 @@ export default {
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
margin-bottom: 8px; margin-bottom: 8px;
position: relative;
} }
.region-selector { .search-input-group {
display: flex; display: flex;
justify-content: space-between;
gap: 8px; gap: 8px;
align-items: stretch;
height: 32px;
position: relative;
z-index: 1;
} }
.map-picker-input { .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%; 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 { .map-container {
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
border-radius: 4px; border-radius: 4px;
@ -674,6 +631,37 @@ export default {
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); 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 { .form-item-tip {
margin-top: 8px; margin-top: 8px;
font-size: 0.8em; font-size: 0.8em;
@ -693,7 +681,38 @@ export default {
padding: 6px 12px; padding: 6px 12px;
} }
:deep(.ant-input-affix-wrapper),
:deep(.ant-btn) {
height: 32px;
}
:deep(.ant-input-affix-wrapper) { :deep(.ant-input-affix-wrapper) {
border-radius: 4px; 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> </style>