388 lines
10 KiB
Plaintext
388 lines
10 KiB
Plaintext
<div id="map" style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;"></div>
|
||
|
||
<?php /* Верхняя панель поиска */ ?>
|
||
<div id="search-bar"
|
||
class="container-fluid py-3"
|
||
style="position:absolute;top:0;left:0;right:0;z-index:100;">
|
||
<div class="row">
|
||
<div class="col-md-6 mx-auto position-relative">
|
||
|
||
<?php /* Поле ввода */ ?>
|
||
<input type="text"
|
||
id="address-input"
|
||
class="form-control form-control-lg shadow-lg"
|
||
style="border-radius:50px;font-size:28px;padding:20px 60px 20px 28px;"
|
||
placeholder="Введите адрес (дом, улица)…"
|
||
autocomplete="off">
|
||
|
||
<?php /* Кнопка очистки */ ?>
|
||
<div id="clear-input-btn"
|
||
style="
|
||
position:absolute;
|
||
right:28px;
|
||
top:50%;
|
||
transform:translateY(-50%);
|
||
font-size:38px;
|
||
line-height:32px;
|
||
cursor:pointer;
|
||
display:none;
|
||
color:#666;
|
||
z-index:300;
|
||
">
|
||
×
|
||
</div>
|
||
|
||
<?php /* Подсказки */ ?>
|
||
<div id="suggest-box"
|
||
class="list-group shadow position-absolute w-100"
|
||
style="z-index:200; display:none; max-height:300px; overflow-y:auto;
|
||
top:70px;border-radius:16px;">
|
||
</div>
|
||
|
||
<?php /* Статус ожидания */ ?>
|
||
<div id="typing-status"
|
||
class="mt-2 text-center text-light"
|
||
style="display:none;text-shadow:0 0 3px black;">
|
||
Поиск ближайших адресов…
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
<?php /* Модальное окно результата */ ?>
|
||
<div id="result-modal"
|
||
class="modal fade"
|
||
tabindex="-1"
|
||
role="dialog">
|
||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||
<div class="modal-content shadow-lg" style="border-radius:20px;">
|
||
<div class="modal-body" id="modal-content-area" style="font-size:20px;"></div>
|
||
<div class="modal-footer text-center justify-content-between">
|
||
<button class="btn btn-secondary btn-lg px-5" onclick="printModal()">
|
||
<i class="fas fa-print mr-2"></i> Распечатать
|
||
</button>
|
||
<button class="btn btn-primary btn-lg px-5" data-dismiss="modal">
|
||
Закрыть
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU&apikey=<?php echo \htmlentities($apikey, ENT_QUOTES, 'UTF-8', false); ?>"></script>
|
||
|
||
<script>
|
||
/* -----------------------------------------------------------
|
||
КОНСТАНТЫ
|
||
----------------------------------------------------------- */
|
||
const SUGGEST_KEY = "<?php echo \htmlentities($suggest_apikey, ENT_QUOTES, 'UTF-8', false); ?>";
|
||
const API_SUGGEST = "https://suggest-maps.yandex.ru/v1/suggest";
|
||
const SEARCH_CENTER = [59.859605, 30.340733];
|
||
const API_URL = "?cat=<?php echo \htmlentities($cat->cat, ENT_QUOTES, 'UTF-8', false); ?>&act=getInfo";
|
||
const SUGGEST_DELAY = 500;
|
||
const BBOX = "30.25163,59.81455~30.42983,59.90465";
|
||
|
||
let map, marker;
|
||
let typingTimer = null;
|
||
let lastTypedText = "";
|
||
let polygons = {};
|
||
|
||
/* -----------------------------------------------------------
|
||
КАРТА
|
||
----------------------------------------------------------- */
|
||
ymaps.ready(function () {
|
||
map = new ymaps.Map('map', {
|
||
center: SEARCH_CENTER,
|
||
zoom: 13,
|
||
controls: [], // ← УБРАЛИ ВСЕ КОНТРОЛЫ
|
||
behaviors: ['drag', 'scrollZoom']
|
||
});
|
||
|
||
// Рендер существующих участков
|
||
<?php if(!empty($rows)): ?>
|
||
<?php $__currentLoopData = $rows; $this->addLoop($__currentLoopData);$this->getFirstLoop();
|
||
foreach($__currentLoopData as $row): $loop = $this->incrementLoopIndices(); ?>
|
||
addPolygon(
|
||
<?php echo $row['coords']; ?>,
|
||
<?php echo \htmlentities((int)$row['id'], ENT_QUOTES, 'UTF-8', false); ?>,
|
||
'<?php echo \htmlentities($row['color'], ENT_QUOTES, 'UTF-8', false); ?>'
|
||
);
|
||
<?php endforeach; $this->popLoop(); $loop = $this->getFirstLoop(); ?>
|
||
<?php endif; ?>
|
||
});
|
||
|
||
|
||
function addPolygon(coords, id, color) {
|
||
if (!coords || !coords.length) return;
|
||
|
||
var polygon = new ymaps.Polygon(
|
||
coords,
|
||
{ polygon_id: id },
|
||
{
|
||
fillColor: color || '#00AAFF55',
|
||
strokeColor: '#0000FF',
|
||
strokeWidth: 2,
|
||
opacity: 0.6
|
||
}
|
||
);
|
||
|
||
polygon.events.add('click', function () {
|
||
var pid = polygon.properties.get('polygon_id');
|
||
openPolygonInfo(pid);
|
||
});
|
||
|
||
map.geoObjects.add(polygon);
|
||
polygons[id] = polygon;
|
||
}
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
ЭЛЕМЕНТЫ
|
||
----------------------------------------------------------- */
|
||
var $input = $("#address-input");
|
||
var $suggest = $("#suggest-box");
|
||
var $typingStatus = $("#typing-status");
|
||
var $clearBtn = $("#clear-input-btn");
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
ФОКУС → ОЧИСТКА ПОЛЯ
|
||
----------------------------------------------------------- */
|
||
$input.on("focus", function () {
|
||
if ($(this).val().length > 0) {
|
||
$(this).val("");
|
||
$suggest.hide();
|
||
$typingStatus.hide();
|
||
$clearBtn.hide();
|
||
}
|
||
});
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
КРЕСТИК
|
||
----------------------------------------------------------- */
|
||
$input.on("input", function () {
|
||
if ($(this).val().length > 0) {
|
||
$clearBtn.show();
|
||
} else {
|
||
$clearBtn.hide();
|
||
$suggest.hide();
|
||
$typingStatus.hide();
|
||
}
|
||
});
|
||
|
||
$clearBtn.on("click", function () {
|
||
$input.val("");
|
||
$input.focus();
|
||
$suggest.hide();
|
||
$typingStatus.hide();
|
||
$clearBtn.hide();
|
||
});
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
ВВОД + ПОИСК ПОДСКАЗОК
|
||
----------------------------------------------------------- */
|
||
$input.on("input", function () {
|
||
var text = $(this).val().trim();
|
||
|
||
if (text.length < 4) {
|
||
$suggest.hide();
|
||
$typingStatus.hide();
|
||
return;
|
||
}
|
||
|
||
$typingStatus.show();
|
||
$suggest.hide();
|
||
lastTypedText = text;
|
||
|
||
clearTimeout(typingTimer);
|
||
typingTimer = setTimeout(function () {
|
||
loadSuggest(lastTypedText);
|
||
}, SUGGEST_DELAY);
|
||
});
|
||
|
||
|
||
function loadSuggest(query) {
|
||
$.getJSON(API_SUGGEST, {
|
||
"apikey": SUGGEST_KEY,
|
||
"text": query,
|
||
"lang": "ru_RU",
|
||
"types": "house",
|
||
"bbox": BBOX,
|
||
"ll": SEARCH_CENTER[1] + "," + SEARCH_CENTER[0],
|
||
"strict_bounds": 1
|
||
}, function (data) {
|
||
$typingStatus.hide();
|
||
renderSuggest(data.results || []);
|
||
|
||
}).fail(function(){
|
||
$typingStatus.hide();
|
||
renderSuggestError("Сервис подсказок недоступен");
|
||
});
|
||
}
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
РЕНДЕР ПОДСКАЗОК
|
||
----------------------------------------------------------- */
|
||
function renderSuggest(list) {
|
||
$suggest.empty();
|
||
|
||
if (!list.length) {
|
||
$suggest.html(
|
||
'<div class="list-group-item py-3 text-muted text-center">Адресов не найдено</div>'
|
||
).show();
|
||
return;
|
||
}
|
||
|
||
list.forEach(function (item) {
|
||
var title = item.title?.text || "";
|
||
var subtitle = item.subtitle?.text || "";
|
||
var full = title + (subtitle ? ", " + subtitle : "");
|
||
|
||
$suggest.append(
|
||
'<a href="#" class="list-group-item list-group-item-action py-3" data-address="' + full + '">' +
|
||
'<div class="font-weight-bold">' + title + '</div>' +
|
||
'<div class="small text-muted">' + subtitle + '</div>' +
|
||
'</a>'
|
||
);
|
||
});
|
||
|
||
$suggest.show();
|
||
}
|
||
|
||
|
||
function renderSuggestError(msg) {
|
||
$suggest.empty().html(
|
||
'<div class="list-group-item py-3 text-danger text-center">' + msg + '</div>'
|
||
).show();
|
||
}
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
КЛИК ПО ПОДСКАЗКЕ → ГЕОКОДЕР
|
||
----------------------------------------------------------- */
|
||
$(document).on("click", "#suggest-box a", function (e) {
|
||
e.preventDefault();
|
||
|
||
var addr = $(this).attr("data-address");
|
||
$input.val(addr);
|
||
$suggest.hide();
|
||
|
||
ymaps.geocode(addr, { results: 1 }).then(function (res) {
|
||
var obj = res.geoObjects.get(0);
|
||
if (!obj) {
|
||
showModal("<div class='text-danger'>Не удалось определить координаты</div>");
|
||
return;
|
||
}
|
||
|
||
var coords = obj.geometry.getCoordinates();
|
||
placeMarker(coords, addr);
|
||
sendToServer(coords);
|
||
|
||
}).catch(function(){
|
||
showModal("<div class='text-danger'>Ошибка геокодирования</div>");
|
||
});
|
||
});
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
МАРКЕР
|
||
----------------------------------------------------------- */
|
||
function placeMarker(coords, caption) {
|
||
if (marker) map.geoObjects.remove(marker);
|
||
|
||
marker = new ymaps.Placemark(coords, {
|
||
iconCaption: caption
|
||
}, { preset: 'islands#redIcon' });
|
||
|
||
map.geoObjects.add(marker);
|
||
map.panTo(coords, { flying:true });
|
||
}
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
ОТПРАВКА НА СЕРВЕР
|
||
----------------------------------------------------------- */
|
||
function sendToServer(coords) {
|
||
$.post(API_URL, { coords: coords }, function (html) {
|
||
showModal(html);
|
||
}).fail(function(){
|
||
showModal("<p class='text-danger'>Нет связи с сервером</p>");
|
||
});
|
||
}
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
ОТКРЫТИЕ ПО ПОЛИГОНУ
|
||
----------------------------------------------------------- */
|
||
function openPolygonInfo(id) {
|
||
$.post(API_URL, { polygon_id: id }, function (html) {
|
||
showModal(html);
|
||
}).fail(function () {
|
||
showModal("<p class='text-danger'>Нет связи с сервером</p>");
|
||
});
|
||
}
|
||
|
||
|
||
/* -----------------------------------------------------------
|
||
МОДАЛКА
|
||
----------------------------------------------------------- */
|
||
function showModal(content) {
|
||
$("#modal-content-area").html(content);
|
||
$("#result-modal").modal("show");
|
||
}
|
||
</script>
|
||
|
||
|
||
<style>
|
||
#address-input {
|
||
height: 60px !important;
|
||
font-size: 28px;
|
||
border-radius: 40px;
|
||
}
|
||
|
||
#suggest-box a {
|
||
font-size: 22px;
|
||
border-left: 0; border-right:0;
|
||
}
|
||
|
||
.modal-content {
|
||
font-size: 20px;
|
||
}
|
||
</style>
|
||
|
||
|
||
<script>
|
||
function printModal() {
|
||
let modalBody = document.querySelector("#modal-content-area");
|
||
|
||
if (!modalBody) return;
|
||
|
||
// Сохраняем оригинальный DOM
|
||
let originalContent = document.body.innerHTML;
|
||
|
||
// Подменяем HTML на печатный
|
||
document.body.innerHTML =
|
||
'<div style="padding:20px;">' +
|
||
modalBody.innerHTML +
|
||
'</div>';
|
||
|
||
|
||
window.print();
|
||
|
||
// Возвращаем оригинальный DOM
|
||
document.body.innerHTML = originalContent;
|
||
|
||
// Восстанавливаем JS-обработчики (нужно переинициализировать Bootstrap)
|
||
setTimeout(function () {
|
||
location.reload(); // лёгкий способ всё вернуть без побочных эффектов
|
||
}, 200);
|
||
}
|
||
</script>
|