This commit is contained in:
2025-04-15 12:16:17 +03:30
parent 57d328bbfe
commit 47de2bf54c
20 changed files with 1557 additions and 195 deletions
+122
View File
@@ -0,0 +1,122 @@
<template>
<div class="map-container">
<!-- Search box -->
<div class="search-container">
<input v-model="searchQuery" @keyup.enter="handleSearch" placeholder="جستجو" class="search-input" />
</div>
<l-map ref="map" v-model:zoom="zoom" :center="center" :use-global-leaflet="false" @click="handleClick($event)">
<!-- Base layers -->
<l-control-layers position="topright" />
<!-- OpenStreetMap layer (default) -->
<l-tile-layer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" layer-type="base" name="نمای نقشه ای" :attribution="attribution" />
<!-- Satellite layer (optional) -->
<l-tile-layer
:visible="false"
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
layer-type="base"
name="نمای ماهواره ای"
attribution="Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
/>
<!-- Marker -->
<l-marker v-if="marker.length > 0" :lat-lng="marker">
<l-popup> نقطه انتخاب شده </l-popup>
</l-marker>
</l-map>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import 'leaflet/dist/leaflet.css';
import { LMap, LTileLayer, LMarker, LPopup, LControlLayers } from '@vue-leaflet/vue-leaflet';
import axios from 'axios';
// Props and emits
const props = defineProps({
modelValue: {
type: Array,
default: () => []
}
});
const emit = defineEmits(['update:modelValue']);
// Map configuration
const zoom = ref(13);
const center = ref([31.3183, 48.6706]);
const attribution = ref('© OpenStreetMap contributors');
const marker = ref([...props.modelValue]);
const map = ref(null);
// Search functionality
const searchQuery = ref('');
const handleSearch = async () => {
if (!searchQuery.value) return;
try {
const response = await axios.get(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQuery.value)}`);
if (response.data && response.data.length > 0) {
const { lat, lon } = response.data[0];
center.value = [parseFloat(lat), parseFloat(lon)];
zoom.value = 16;
marker.value = [parseFloat(lat), parseFloat(lon)];
emit('update:modelValue', marker.value);
}
} catch (error) {
console.error('Search failed:', error);
}
};
// Watch for changes in modelValue and sync with marker
watch(
() => props.modelValue,
(newValue) => {
marker.value = [...newValue];
},
{ immediate: true }
);
// Update marker and emit changes to modelValue
const handleClick = (event) => {
marker.value = [event.latlng.lat, event.latlng.lng];
emit('update:modelValue', marker.value);
};
</script>
<style scoped>
.map-container {
font-family: 'Estedad', sans-serif !important;
height: 500px;
width: 100%;
position: relative;
}
.leaflet-container {
font-family: 'Estedad', serif !important;
}
.search-container {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
background: white;
padding: 5px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.search-input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
width: 250px;
font-family: 'Estedad', sans-serif;
}
</style>