Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a0422aa00 | |||
| 00bde46c5e | |||
| 133a8721bb |
+57
-4
@@ -111,11 +111,13 @@ html, body {
|
|||||||
|
|
||||||
#close-viewer-btn {
|
#close-viewer-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20px;
|
top: 5px;
|
||||||
left: 60px;
|
left: 40px;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
border: none;
|
border: white solid 1px;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -125,6 +127,57 @@ html, body {
|
|||||||
|
|
||||||
#close-viewer-btn:hover {
|
#close-viewer-btn:hover {
|
||||||
background: white;
|
background: white;
|
||||||
|
color: rgba(44, 44, 44, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Viewer Info Overlay */
|
||||||
|
#viewer-info-overlay {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 15px;
|
||||||
|
right: 15px;
|
||||||
|
background: rgba(30, 30, 30, 0.5); /* Nền xám tối transparent 0.5 - Tăng độ rõ */
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.5); /* Viền trắng mờ - Tăng độ rõ */
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px 20px;
|
||||||
|
max-width: 300px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
|
||||||
|
z-index: 3002; /* Trên viewer, dưới các modal */
|
||||||
|
pointer-events: none; /* Không chặn tương tác chuột với viewer */
|
||||||
|
opacity: 0; /* Khởi tạo ẩn */
|
||||||
|
transform: translateY(20px); /* Hiệu ứng trượt lên */
|
||||||
|
transition: opacity 0.3s ease-out, transform 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer-info-overlay.show {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer-info-overlay .info-content h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #00d4ff; /* Màu xanh nổi bật cho tiêu đề */
|
||||||
|
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer-info-overlay .info-content p {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #ccc;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer-info-overlay .info-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #aaa;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
/* Modal Overlay */
|
/* Modal Overlay */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
|
|||||||
+27
-3
@@ -518,16 +518,40 @@
|
|||||||
<div id="hotspot-modal" class="modal-overlay" style="display: none; z-index: 3000;">
|
<div id="hotspot-modal" class="modal-overlay" style="display: none; z-index: 3000;">
|
||||||
<div class="modal-content action-modal-content logout-modal-dark" style="max-width: 500px; border-top: 4px solid #ffc107;">
|
<div class="modal-content action-modal-content logout-modal-dark" style="max-width: 500px; border-top: 4px solid #ffc107;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3 id="hotspot-modal-title">Biên tập điểm điều hướng</h3>
|
<h3 id="hotspot-modal-title">Thêm/sửa điểm điều hướng</h3>
|
||||||
<span class="close-btn" onclick="closeHotspotModal()">×</span>
|
<span class="close-btn" onclick="closeHotspotModal()">×</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="hotspot-form">
|
<form id="hotspot-form">
|
||||||
<!-- Tọa độ ẩn để xử lý GPS và vị trí -->
|
<!-- Tọa độ ẩn để xử lý GPS và vị trí -->
|
||||||
<input type="hidden" id="hs-pitch">
|
<div class="form-group" style="background: rgba(255,193,7,0.05); padding: 10px; border-radius: 6px; border: 1px dashed #ffc107; margin-bottom: 15px;">
|
||||||
<input type="hidden" id="hs-yaw">
|
<label style="color: #ffc107; font-size: 12px; margin-bottom: 8px;">Vị trí hiển thị (Pitch/Yaw):</label>
|
||||||
|
<div style="display: flex; gap: 10px; align-items: center;">
|
||||||
|
<input type="text" id="hs-pitch" readonly style="flex: 1; background: #222; border: 1px solid #444; color: #fff; text-align: center; font-family: monospace;">
|
||||||
|
<input type="text" id="hs-yaw" readonly style="flex: 1; background: #222; border: 1px solid #444; color: #fff; text-align: center; font-family: monospace;">
|
||||||
|
<button type="button" onclick="updateHotspotCoordsFromView()" class="edit-btn-small" style="background: #007bff; white-space: nowrap; height: 34px; padding: 0 10px;">
|
||||||
|
<i class="fas fa-crosshairs"></i> Lấy tọa độ hiện tại
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<small style="display: block; color: #888; font-size: 10px; margin-top: 5px;">* Xoay ảnh đến vị trí mong muốn rồi nhấn nút để cập nhật điểm đặt bong bóng.</small>
|
||||||
|
</div>
|
||||||
<input type="hidden" id="hs-id">
|
<input type="hidden" id="hs-id">
|
||||||
|
|
||||||
|
<div class="form-group divider">
|
||||||
|
<label>Hành động:</label>
|
||||||
|
<div class="radio-group">
|
||||||
|
<label class="radio-item"><input type="radio" name="hsActionMode" value="create" checked onclick="toggleHSActionMode('create')"> Thêm mới</label>
|
||||||
|
<label class="radio-item"><input type="radio" name="hsActionMode" value="edit" onclick="toggleHSActionMode('edit')"> Sửa điểm có sẵn</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="hs-edit-select-container" class="form-group" style="display: none; background: rgba(0, 123, 255, 0.1); padding: 10px; border-radius: 6px; border: 1px solid #007bff;">
|
||||||
|
<label for="hs-to-edit-id" style="color: #00d4ff;">Chọn điểm để sửa:</label>
|
||||||
|
<select id="hs-to-edit-id" onchange="onSelectHotspotToEdit(this.value)" style="background: #111; color: #fff; border: 1px solid #007bff;">
|
||||||
|
<option value="">-- Chọn điểm trong Viewer --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="hs-title">Tiêu đề (Label)</label>
|
<label for="hs-title">Tiêu đề (Label)</label>
|
||||||
<input type="text" id="hs-title" name="title" placeholder="Ví dụ: Cổng vào, Phòng khách..." required>
|
<input type="text" id="hs-title" name="title" placeholder="Ví dụ: Cổng vào, Phòng khách..." required>
|
||||||
|
|||||||
+164
-18
@@ -369,6 +369,14 @@ function formatSystemDate(dateString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kiểm tra xem người dùng có đang dùng thiết bị di động hay không
|
||||||
|
*/
|
||||||
|
function isMobileDevice() {
|
||||||
|
return (window.innerWidth <= 768) ||
|
||||||
|
(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the full-screen Leaflet Map
|
* Initializes the full-screen Leaflet Map
|
||||||
*/
|
*/
|
||||||
@@ -388,7 +396,11 @@ function initMap() {
|
|||||||
if (isNaN(startZoom)) startZoom = 13;
|
if (isNaN(startZoom)) startZoom = 13;
|
||||||
|
|
||||||
// Khởi tạo bản đồ với zoomControl và tắt attribution mặc định của Leaflet
|
// Khởi tạo bản đồ với zoomControl và tắt attribution mặc định của Leaflet
|
||||||
map = L.map('map', { zoomControl: true, attributionControl: false }).setView([startLat, startLng], startZoom);
|
map = L.map('map', {
|
||||||
|
zoomControl: !isMobileDevice(), // Ẩn nút +/- trên mobile để lấy thêm không gian hiển thị
|
||||||
|
attributionControl: false,
|
||||||
|
tap: true // Hỗ trợ click trên thiết bị cảm ứng tốt hơn
|
||||||
|
}).setView([startLat, startLng], startZoom);
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 19,
|
maxZoom: 19,
|
||||||
@@ -1056,7 +1068,7 @@ async function loadScenes(urlToken = null) {
|
|||||||
let thumbUrl = `${API_BASE_URL}/assets/view/${assetId}`;
|
let thumbUrl = `${API_BASE_URL}/assets/view/${assetId}`;
|
||||||
if (token) thumbUrl += `?token=${token}`;
|
if (token) thumbUrl += `?token=${token}`;
|
||||||
else if (scene.privacy === 'shared' && scene.shareToken) thumbUrl += `?token=${scene.shareToken}`;
|
else if (scene.privacy === 'shared' && scene.shareToken) thumbUrl += `?token=${scene.shareToken}`;
|
||||||
thumbHtml = `<img src="${thumbUrl}" alt="${sceneName}">`;
|
thumbHtml = `<img src="${thumbUrl}" alt="${sceneName}" loading="lazy">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const calloutIcon = L.divIcon({
|
const calloutIcon = L.divIcon({
|
||||||
@@ -1423,6 +1435,9 @@ async function openScene(sceneId, privacy, shareToken, force = false, initialPit
|
|||||||
// Initialize 3D Viewer with secure, referer-protected image stream
|
// Initialize 3D Viewer with secure, referer-protected image stream
|
||||||
initPanoramaViewer(secureImageUrl, hotspots || [], sceneOwnerId, initialPitch, initialYaw);
|
initPanoramaViewer(secureImageUrl, hotspots || [], sceneOwnerId, initialPitch, initialYaw);
|
||||||
|
|
||||||
|
// Hiển thị thông tin overlay góc màn hình
|
||||||
|
updateViewerInfoOverlay(scene);
|
||||||
|
|
||||||
// Sau khi mở thành công từ URL trực tiếp, xóa tham số để làm sạch thanh địa chỉ (URL chuyên nghiệp)
|
// Sau khi mở thành công từ URL trực tiếp, xóa tham số để làm sạch thanh địa chỉ (URL chuyên nghiệp)
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
if (urlParams.has('sceneId') || window.location.pathname.includes('/api/share/')) {
|
if (urlParams.has('sceneId') || window.location.pathname.includes('/api/share/')) {
|
||||||
@@ -1453,6 +1468,72 @@ async function openScene(sceneId, privacy, shareToken, force = false, initialPit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cập nhật overlay thông tin cảnh đang xem (Góc dưới bên trái)
|
||||||
|
*/
|
||||||
|
async function updateViewerInfoOverlay(scene) {
|
||||||
|
if (!scene) {
|
||||||
|
console.warn("updateViewerInfoOverlay: Scene object is null or undefined. Cannot display info.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let overlay = document.getElementById('viewer-info-overlay');
|
||||||
|
if (!overlay) {
|
||||||
|
overlay = document.createElement('div');
|
||||||
|
overlay.id = 'viewer-info-overlay';
|
||||||
|
// Append to body to allow independent positioning and animation
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lang = systemSettings.language || 'vi';
|
||||||
|
const name = scene.name || scene.title || (lang === 'vi' ? "Cảnh không tên" : "Untitled Scene");
|
||||||
|
const desc = scene.description || "";
|
||||||
|
const author = scene.createdBy?.username || (lang === 'vi' ? "Ẩn danh" : "Anonymous");
|
||||||
|
const date = formatSystemDate(scene.createdAt);
|
||||||
|
|
||||||
|
// Lấy tọa độ để truy vấn địa chỉ
|
||||||
|
const lat = scene.gps?.lat || scene.lat;
|
||||||
|
const lng = scene.gps?.lng || scene.lng;
|
||||||
|
let locationText = lang === 'vi' ? "Đang xác định vị trí..." : "Locating...";
|
||||||
|
|
||||||
|
overlay.innerHTML = `
|
||||||
|
<div class="info-content">
|
||||||
|
<h4>${name}</h4>
|
||||||
|
${desc ? `<p>${desc}</p>` : ''}
|
||||||
|
<div class="info-meta">
|
||||||
|
<span>👤 ${author}</span>
|
||||||
|
<span>📸 ${lang === 'vi' ? 'Ngày chụp' : 'Date taken'}: ${date}</span>
|
||||||
|
<span id="overlay-address">📍 ${locationText}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
overlay.classList.add('show'); // Make it visible with transition
|
||||||
|
|
||||||
|
// Thực hiện Reverse Geocoding để lấy địa chỉ từ tọa độ
|
||||||
|
try {
|
||||||
|
const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`);
|
||||||
|
const data = await res.json();
|
||||||
|
const address = data.display_name || `${lat.toFixed(4)}, ${lng.toFixed(4)}`;
|
||||||
|
const addrElem = document.getElementById('overlay-address');
|
||||||
|
if (addrElem) addrElem.innerText = `📍 ${address}`;
|
||||||
|
} catch (e) {
|
||||||
|
const addrElem = document.getElementById('overlay-address');
|
||||||
|
if (addrElem) addrElem.innerText = `📍 ${lat.toFixed(4)}, ${lng.toFixed(4)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ẩn overlay thông tin cảnh đang xem
|
||||||
|
*/
|
||||||
|
window.hideViewerInfoOverlay = function() {
|
||||||
|
const overlay = document.getElementById('viewer-info-overlay');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.classList.remove('show'); // Hide it with transition
|
||||||
|
// Optionally remove from DOM after transition if not needed
|
||||||
|
// setTimeout(() => overlay.remove(), 300); // Match CSS transition duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Khôi phục Scene đang xem từ localStorage sau khi reload trang
|
* Khôi phục Scene đang xem từ localStorage sau khi reload trang
|
||||||
*/
|
*/
|
||||||
@@ -1467,6 +1548,24 @@ function restoreActiveScene() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cập nhật tọa độ Pitch/Yaw cho hotspot từ góc nhìn trung tâm hiện tại của Viewer
|
||||||
|
*/
|
||||||
|
window.updateHotspotCoordsFromView = function() {
|
||||||
|
// activeViewer được quản lý trong viewer360.js
|
||||||
|
if (typeof activeViewer !== 'undefined' && activeViewer) {
|
||||||
|
const pitch = activeViewer.getPitch();
|
||||||
|
const yaw = activeViewer.getYaw();
|
||||||
|
|
||||||
|
document.getElementById('hs-pitch').value = pitch.toFixed(2);
|
||||||
|
document.getElementById('hs-yaw').value = yaw.toFixed(2);
|
||||||
|
|
||||||
|
showNotification(`Đã ghi nhận vị trí mới: Pitch ${pitch.toFixed(2)}, Yaw ${yaw.toFixed(2)}`, 'success');
|
||||||
|
} else {
|
||||||
|
showNotification("Viewer không hoạt động, không thể lấy tọa độ.", "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Xử lý việc tạo hotspot sau khi click chuột phải trong trình xem 360
|
* Xử lý việc tạo hotspot sau khi click chuột phải trong trình xem 360
|
||||||
* @param {number} pitch - Tọa độ dọc (-90 đến 90)
|
* @param {number} pitch - Tọa độ dọc (-90 đến 90)
|
||||||
@@ -1486,14 +1585,34 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
|
|||||||
// Hiển thị Modal TRƯỚC để các logic UI (như Mini Map) tính toán được kích thước
|
// Hiển thị Modal TRƯỚC để các logic UI (như Mini Map) tính toán được kích thước
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
|
|
||||||
// Reset form và gán tọa độ
|
// Reset form và gán tọa độ click ban đầu
|
||||||
form.reset();
|
form.reset();
|
||||||
document.getElementById('hs-pitch').value = pitch;
|
document.getElementById('hs-pitch').value = pitch;
|
||||||
document.getElementById('hs-yaw').value = yaw;
|
document.getElementById('hs-yaw').value = yaw;
|
||||||
document.getElementById('hs-id').value = existingHotspot ? existingHotspot._id : '';
|
document.getElementById('hs-id').value = existingHotspot ? existingHotspot._id : '';
|
||||||
document.getElementById('hotspot-modal-title').innerText = existingHotspot ? 'Cập nhật điểm điều hướng' : 'Thêm điểm điều hướng mới';
|
document.getElementById('hotspot-modal-title').innerText = 'Thêm/sửa điểm điều hướng';
|
||||||
|
|
||||||
|
// Nạp danh sách hotspot hiện có trong Viewer vào dropdown chỉnh sửa
|
||||||
|
const editSelect = document.getElementById('hs-to-edit-id');
|
||||||
|
editSelect.innerHTML = '<option value="">-- Chọn điểm trong Viewer --</option>';
|
||||||
|
if (typeof currentHotspots !== 'undefined' && currentHotspots.length > 0) {
|
||||||
|
currentHotspots.forEach(h => {
|
||||||
|
editSelect.innerHTML += `<option value="${h._id}">${h.title || 'Không tiêu đề'} (ID: ...${h._id.slice(-4)})</option>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Reset UI states
|
// Reset UI states
|
||||||
|
if (existingHotspot) {
|
||||||
|
document.querySelector('input[name="hsActionMode"][value="edit"]').checked = true;
|
||||||
|
toggleHSActionMode('edit');
|
||||||
|
editSelect.value = existingHotspot._id;
|
||||||
|
// Điền dữ liệu của hotspot được click vào form
|
||||||
|
onSelectHotspotToEdit(existingHotspot._id);
|
||||||
|
} else {
|
||||||
|
document.querySelector('input[name="hsActionMode"][value="create"]').checked = true;
|
||||||
|
toggleHSActionMode('create');
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelector('input[name="hsLinkType"][value="existing"]').checked = true;
|
document.querySelector('input[name="hsLinkType"][value="existing"]').checked = true;
|
||||||
window.toggleHSLinkType('existing');
|
window.toggleHSLinkType('existing');
|
||||||
document.querySelector('input[name="hsGPSMode"][value="map"]').checked = true;
|
document.querySelector('input[name="hsGPSMode"][value="map"]').checked = true;
|
||||||
@@ -1536,17 +1655,6 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
|
|||||||
if (existingNotice) existingNotice.style.opacity = '1';
|
if (existingNotice) existingNotice.style.opacity = '1';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// QUAN TRỌNG: Chỉ điền dữ liệu hotspot cũ SAU KHI dropdown đã được nạp đầy đủ options
|
|
||||||
if (existingHotspot) {
|
|
||||||
document.getElementById('hs-title').value = existingHotspot.title || '';
|
|
||||||
document.getElementById('hs-desc').value = existingHotspot.description || '';
|
|
||||||
if (existingHotspot.target_scene_id) {
|
|
||||||
select.value = existingHotspot.target_scene_id;
|
|
||||||
// Kích hoạt logic hiển thị thông báo ngay khi mở modal nếu đang sửa
|
|
||||||
if (typeof select.onchange === 'function') select.onchange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) { console.error("Lỗi nạp danh sách scene:", e); }
|
} catch (e) { console.error("Lỗi nạp danh sách scene:", e); }
|
||||||
|
|
||||||
// Xử lý sự kiện submit form
|
// Xử lý sự kiện submit form
|
||||||
@@ -1554,6 +1662,7 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
const linkType = formData.get('hsLinkType');
|
const linkType = formData.get('hsLinkType');
|
||||||
|
const hotspotId = document.getElementById('hs-id').value;
|
||||||
|
|
||||||
if (linkType === 'upload') {
|
if (linkType === 'upload') {
|
||||||
const file = document.getElementById('hs-panorama-file').files[0];
|
const file = document.getElementById('hs-panorama-file').files[0];
|
||||||
@@ -1592,7 +1701,7 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
|
|||||||
if (activeTourId) sceneData.append('tourId', activeTourId);
|
if (activeTourId) sceneData.append('tourId', activeTourId);
|
||||||
|
|
||||||
uploadWithProgress(`${API_BASE_URL}/scenes`, 'POST', sceneData, token, 'hs', async (sceneRes) => {
|
uploadWithProgress(`${API_BASE_URL}/scenes`, 'POST', sceneData, token, 'hs', async (sceneRes) => {
|
||||||
await saveHotspotToDB(pitch, yaw, formData.get('title'), formData.get('description'), sceneRes.scene._id, existingHotspot?._id);
|
await saveHotspotToDB(pitch, yaw, formData.get('title'), formData.get('description'), sceneRes.scene._id, hotspotId);
|
||||||
closeHotspotModal();
|
closeHotspotModal();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -1603,11 +1712,48 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
|
|||||||
showNotification('Vui lòng chọn cảnh để liên kết.', 'warning');
|
showNotification('Vui lòng chọn cảnh để liên kết.', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await saveHotspotToDB(pitch, yaw, formData.get('title'), formData.get('description'), finalTargetId, existingHotspot?._id);
|
await saveHotspotToDB(pitch, yaw, formData.get('title'), formData.get('description'), finalTargetId, hotspotId);
|
||||||
closeHotspotModal();
|
closeHotspotModal();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chuyển đổi giữa chế độ Thêm mới và Sửa điểm có sẵn
|
||||||
|
*/
|
||||||
|
window.toggleHSActionMode = function(mode) {
|
||||||
|
const selectContainer = document.getElementById('hs-edit-select-container');
|
||||||
|
selectContainer.style.display = mode === 'edit' ? 'block' : 'none';
|
||||||
|
|
||||||
|
if (mode === 'create') {
|
||||||
|
document.getElementById('hs-id').value = '';
|
||||||
|
// Giữ nguyên pitch/yaw vừa click, chỉ reset text
|
||||||
|
document.getElementById('hs-title').value = '';
|
||||||
|
document.getElementById('hs-desc').value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Điền thông tin khi người dùng chọn một hotspot từ danh sách để sửa
|
||||||
|
*/
|
||||||
|
window.onSelectHotspotToEdit = function(id) {
|
||||||
|
if (!id) return;
|
||||||
|
// currentHotspots được quản lý trong viewer360.js
|
||||||
|
const hs = currentHotspots.find(h => h._id === id);
|
||||||
|
if (hs) {
|
||||||
|
document.getElementById('hs-id').value = hs._id;
|
||||||
|
document.getElementById('hs-title').value = hs.title || '';
|
||||||
|
document.getElementById('hs-desc').value = hs.description || '';
|
||||||
|
|
||||||
|
// Giữ nguyên tọa độ pitch/yaw từ điểm vừa click chuột phải
|
||||||
|
// Không ghi đè bằng tọa độ cũ của hotspot để thực hiện việc di chuyển vị trí
|
||||||
|
|
||||||
|
if (hs.target_scene_id) {
|
||||||
|
const targetId = hs.target_scene_id._id || hs.target_scene_id;
|
||||||
|
document.getElementById('hs-target-id').value = targetId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Đóng Modal biên tập Hotspot
|
* Đóng Modal biên tập Hotspot
|
||||||
*/
|
*/
|
||||||
@@ -1886,7 +2032,7 @@ async function loadMyTours() {
|
|||||||
if (assetId) {
|
if (assetId) {
|
||||||
let thumbUrl = `${API_BASE_URL}/assets/view/${assetId}`;
|
let thumbUrl = `${API_BASE_URL}/assets/view/${assetId}`;
|
||||||
if (token) thumbUrl += `?token=${token}`;
|
if (token) thumbUrl += `?token=${token}`;
|
||||||
card.style.backgroundImage = `url('${thumbUrl}')`;
|
card.innerHTML = `<img class="tour-card-bg" src="${thumbUrl}" loading="lazy">`;
|
||||||
} else {
|
} else {
|
||||||
card.style.backgroundColor = '#1a1a1a';
|
card.style.backgroundColor = '#1a1a1a';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,9 +119,13 @@ function initPanoramaViewer(imageUrl, hotspots = [], ownerId = null, initialPitc
|
|||||||
activeViewer = pannellum.viewer('panorama-viewer', {
|
activeViewer = pannellum.viewer('panorama-viewer', {
|
||||||
"type": "equirectangular",
|
"type": "equirectangular",
|
||||||
"panorama": imageUrl,
|
"panorama": imageUrl,
|
||||||
|
"autoRotate": 0,
|
||||||
|
"autoRotateInactivityDelay": 5000,
|
||||||
"autoLoad": true,
|
"autoLoad": true,
|
||||||
"pitch": initialPitch,
|
"pitch": initialPitch,
|
||||||
"yaw": initialYaw,
|
"yaw": initialYaw,
|
||||||
|
"orientationOnByDefault": true,
|
||||||
|
"draggable": true,
|
||||||
"showControls": true,
|
"showControls": true,
|
||||||
"compass": false,
|
"compass": false,
|
||||||
"mouseZoom": true,
|
"mouseZoom": true,
|
||||||
@@ -140,6 +144,11 @@ function initPanoramaViewer(imageUrl, hotspots = [], ownerId = null, initialPitc
|
|||||||
function closeViewer() {
|
function closeViewer() {
|
||||||
document.getElementById('viewer-container').style.display = 'none';
|
document.getElementById('viewer-container').style.display = 'none';
|
||||||
|
|
||||||
|
// Ẩn overlay thông tin cảnh nếu có
|
||||||
|
if (typeof window.hideViewerInfoOverlay === 'function') {
|
||||||
|
window.hideViewerInfoOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
// Xóa trạng thái Scene đang hoạt động khi đóng viewer
|
// Xóa trạng thái Scene đang hoạt động khi đóng viewer
|
||||||
localStorage.removeItem('activeSceneId');
|
localStorage.removeItem('activeSceneId');
|
||||||
localStorage.removeItem('activeScenePrivacy');
|
localStorage.removeItem('activeScenePrivacy');
|
||||||
|
|||||||
Reference in New Issue
Block a user