feat:自提点
This commit is contained in:
1 parent
8dba6a0894
commit
cf8aa56c9c
13 files changed
+1767
-15
No files matched your search
@@ -0,0 +1,257 @@
|
||||
<template>
|
||||
<div class="map-select-container">
|
||||
<el-input v-model="state.selectAddress" :placeholder="props.placeholder" :disabled="props.disabled"
|
||||
:clearable="props.clearable" class="map-select-input" readonly @clear="handleClear">
|
||||
<template #prepend v-if="state.selectedLocation">
|
||||
<el-icon class="location-icon">
|
||||
<Location />
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button @click="handleMapClick" :disabled="props.disabled" class="map-button">
|
||||
<el-icon>
|
||||
<MapLocation />
|
||||
</el-icon>
|
||||
选择位置
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<!-- 坐标信息显示 -->
|
||||
<div v-if="state.selectedLocation && state.showCoordinates" class="coordinates-info">
|
||||
<span class="coordinates-text">
|
||||
经纬度: {{ state.selectedLocation.lng.toFixed(6) }}, {{ state.selectedLocation.lat.toFixed(6) }}
|
||||
</span>
|
||||
<el-button text size="small" @click="copyCoordinates" class="copy-btn">
|
||||
<el-icon>
|
||||
<CopyDocument />
|
||||
</el-icon>
|
||||
复制
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<map-dialog ref="mapDialogRef" @confirm="handleLocationConfirm" @close="handleDialogClose" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="map-location-select">
|
||||
import { reactive, ref, onMounted, watch, defineAsyncComponent } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { MapLocation, Location, CopyDocument } from '@element-plus/icons-vue'
|
||||
|
||||
const MapDialog = defineAsyncComponent(() => import('./map-dialog.vue'))
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
// 定义属性
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: any
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
clearable?: boolean
|
||||
showCoordinates?: boolean
|
||||
returnFormat?: 'object' | 'coordinates' | 'address'
|
||||
}>(),
|
||||
{
|
||||
placeholder: "请点击选择地图位置",
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
showCoordinates: true,
|
||||
returnFormat: 'object'
|
||||
}
|
||||
)
|
||||
|
||||
// 响应式状态
|
||||
const state = reactive<any>({
|
||||
selectAddress: '',
|
||||
selectedLocation: null,
|
||||
selectedData: null
|
||||
})
|
||||
|
||||
// 引用
|
||||
const mapDialogRef = ref()
|
||||
|
||||
// 清空选择
|
||||
const handleClear = () => {
|
||||
state.selectAddress = ''
|
||||
state.selectedLocation = null
|
||||
state.selectedData = null
|
||||
|
||||
emit('update:modelValue', null)
|
||||
emit('change', null)
|
||||
}
|
||||
|
||||
// 打开地图选择器
|
||||
const handleMapClick = () => {
|
||||
if (props.disabled) return
|
||||
|
||||
// 如果已有位置,传递给对话框作为初始位置
|
||||
mapDialogRef.value?.open(state.selectedLocation)
|
||||
}
|
||||
|
||||
// 处理位置确认
|
||||
const handleLocationConfirm = (result: any) => {
|
||||
state.selectAddress = result.address
|
||||
state.selectedLocation = result.location
|
||||
state.selectedData = result
|
||||
|
||||
// 根据返回格式发送不同的值
|
||||
let emitValue
|
||||
switch (props.returnFormat) {
|
||||
case 'coordinates':
|
||||
emitValue = result.coordinates
|
||||
break
|
||||
case 'address':
|
||||
emitValue = result.address
|
||||
break
|
||||
case 'object':
|
||||
default:
|
||||
emitValue = result
|
||||
break
|
||||
}
|
||||
|
||||
emit('update:modelValue', emitValue)
|
||||
emit('change', emitValue)
|
||||
|
||||
ElMessage.success('位置选择成功')
|
||||
}
|
||||
|
||||
// 处理对话框关闭
|
||||
const handleDialogClose = () => {
|
||||
// 对话框关闭时的处理
|
||||
}
|
||||
|
||||
// 监听外部值变化
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (newValue) {
|
||||
if (typeof newValue === 'object' && newValue.address) {
|
||||
state.selectAddress = newValue.address
|
||||
state.selectedLocation = newValue.location
|
||||
state.selectedData = newValue
|
||||
} else if (typeof newValue === 'string') {
|
||||
state.selectAddress = newValue
|
||||
}
|
||||
} else {
|
||||
handleClear()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化
|
||||
})
|
||||
|
||||
// 复制坐标
|
||||
const copyCoordinates = async () => {
|
||||
if (!state.selectedLocation) return
|
||||
|
||||
const coordinates = `${state.selectedLocation.lng},${state.selectedLocation.lat}`
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(coordinates)
|
||||
ElMessage.success('坐标已复制到剪贴板')
|
||||
} catch (error) {
|
||||
// 降级方案
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = coordinates
|
||||
document.body.appendChild(textArea)
|
||||
textArea.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(textArea)
|
||||
ElMessage.success('坐标已复制到剪贴板')
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
open: handleMapClick,
|
||||
clear: handleClear,
|
||||
getLocation: () => state.selectedLocation,
|
||||
getAddress: () => state.selectAddress,
|
||||
getData: () => state.selectedData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.map-select-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.map-select-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.map-select-input :deep(.el-input__inner) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.location-icon {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.map-button {
|
||||
border: none;
|
||||
background: #409eff;
|
||||
color: white;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.map-button:hover {
|
||||
background: #337ecc;
|
||||
}
|
||||
|
||||
.map-button:disabled {
|
||||
background: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.coordinates-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.coordinates-text {
|
||||
color: #606266;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
padding: 4px 8px;
|
||||
height: auto;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.map-button {
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.coordinates-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user