v1.2.11
- [CUSTOMER] Install 요청, 청소 전, 청소 후 사진도 보이도록 수정. multi image 보이도록 수정. - [MAP] Container 교체 요청 시 사진 multi upload & preview. - [INSTALL WAIT LIST] Install request 완료 사진 multi upload & preview. - [INSTALL ORDER] Install 완료 사진 multi upload & preview. 조회 조건 바뀌어도 View Mode 유지되도록 수정.
This commit is contained in:
parent
62342d3454
commit
c48c0a97da
|
|
@ -264,11 +264,10 @@ trait InstallAPI {
|
|||
|
||||
while ($img = mysqli_fetch_assoc($ri)) {
|
||||
|
||||
$imgPath = $img['i_filepath'] . $img['i_filename'];
|
||||
|
||||
$photos[] = [
|
||||
'i_uid' => (int)$img['i_uid'],
|
||||
'imgPath' => $imgPath
|
||||
'i_uid' => (int)$img['i_uid'],
|
||||
'i_filepath' => $img['i_filepath'],
|
||||
'i_filename' => $img['i_filename']
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -1708,20 +1707,19 @@ trait InstallAPI {
|
|||
$fail('select_di', 'daily_install not found');
|
||||
}
|
||||
|
||||
if(!isset($_FILES['photo']) || $_FILES['photo']['error'] !== 0){
|
||||
$fail('file', 'no file');
|
||||
if(!isset($_FILES['photos'])){
|
||||
$fail('file', 'no files');
|
||||
}
|
||||
|
||||
$file = $_FILES['photo'];
|
||||
$customer_uid = intval($di['di_customer_uid']);
|
||||
|
||||
$ok = $this->save_customer_image_single(
|
||||
$ok = $this->save_customer_image_multi(
|
||||
$customer_uid,
|
||||
$member_uid,
|
||||
$i_type,
|
||||
$di_uid, // i_sourceuid = di_uid
|
||||
$di_uid,
|
||||
$note,
|
||||
$file
|
||||
$_FILES['photos']
|
||||
);
|
||||
|
||||
if(!$ok){
|
||||
|
|
@ -1732,7 +1730,7 @@ trait InstallAPI {
|
|||
exit;
|
||||
}
|
||||
|
||||
private function save_customer_image_single($i_customeruid, $i_memberuid, $i_type, $i_sourceuid, $i_note, $file){
|
||||
private function save_customer_image_multi($i_customeruid, $i_memberuid, $i_type, $i_sourceuid, $i_note, $files){
|
||||
|
||||
$conn = $GLOBALS['conn'];
|
||||
|
||||
|
|
@ -1744,36 +1742,48 @@ trait InstallAPI {
|
|||
$i_noteEsc = mysqli_real_escape_string($conn, trim($i_note));
|
||||
$i_createddate = date("YmdHis");
|
||||
|
||||
// 확장자 체크(최소 보안)
|
||||
$allowed = ['jpg','jpeg','png','webp'];
|
||||
$ext = strtolower(pathinfo($file['name'] ?? '', PATHINFO_EXTENSION));
|
||||
if(!$ext || !in_array($ext, $allowed)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 단위 1장 유지: (customeruid + type, sourceuid 는 제외)
|
||||
// 기존 row 비활성화
|
||||
$qry_update = "
|
||||
UPDATE tbl_customer_image
|
||||
SET i_status = 'I'
|
||||
WHERE i_customeruid = '{$i_customeruid}'
|
||||
AND i_type = '{$i_type}'
|
||||
AND i_status = 'A'
|
||||
AND i_type = '{$i_type}'
|
||||
AND i_status = 'A'
|
||||
";
|
||||
if(qry($qry_update) === false) return false;
|
||||
|
||||
// 저장 폴더
|
||||
// 폴더
|
||||
$upload_folder = getenv("DOCUMENT_ROOT")."/upload/customer_image/".$i_customeruid;
|
||||
if(!is_dir($upload_folder)){
|
||||
mkdir($upload_folder, 0777, true);
|
||||
}
|
||||
|
||||
$newFileName = "IMG_".$i_customeruid."_".time().rand(1000,9999)."_0.jpg";
|
||||
$savePath = $upload_folder . "/" . $newFileName;
|
||||
$savedFiles = [];
|
||||
|
||||
if (!$this->compressAndResizeImage($file['tmp_name'], $savePath, $ext, 1200, 75)) {
|
||||
return false;
|
||||
foreach ($files['name'] as $i => $name) {
|
||||
|
||||
if ($files['error'][$i] !== 0) continue;
|
||||
|
||||
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
||||
if(!$ext || !in_array($ext, $allowed)) continue;
|
||||
|
||||
$tmp = $files['tmp_name'][$i];
|
||||
|
||||
$newFileName = "IMG_".$i_customeruid."_".time().rand(1000,9999)."_".$i.".jpg";
|
||||
$savePath = $upload_folder . "/" . $newFileName;
|
||||
|
||||
if (!$this->compressAndResizeImage($tmp, $savePath, $ext, 1200, 75)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$savedFiles[] = $newFileName;
|
||||
}
|
||||
|
||||
if(empty($savedFiles)) return false;
|
||||
|
||||
$fileNameString = implode(',', $savedFiles);
|
||||
$dbFilePath = "/upload/customer_image/".$i_customeruid."/";
|
||||
|
||||
$sqlIns = "
|
||||
|
|
@ -1794,7 +1804,7 @@ trait InstallAPI {
|
|||
'{$i_memberuid}',
|
||||
'{$i_createddate}',
|
||||
'{$i_type}',
|
||||
'{$newFileName}',
|
||||
'{$fileNameString}',
|
||||
'{$dbFilePath}',
|
||||
'A',
|
||||
'{$i_noteEsc}',
|
||||
|
|
|
|||
|
|
@ -2048,137 +2048,116 @@ if( $total_count < 1 ) {
|
|||
|
||||
<p style="clear: both;"></p>
|
||||
<?php
|
||||
// 이미지 가져오기
|
||||
$qry_img = "
|
||||
SELECT *
|
||||
FROM tbl_customer_image tci
|
||||
LEFT OUTER JOIN tbl_member tm ON tci.i_createdby = tm.m_uid
|
||||
WHERE tci.i_customeruid = '$c_uid'
|
||||
AND tci.i_status = 'A'
|
||||
ORDER BY tci.i_uid DESC
|
||||
";
|
||||
$rt_img = $jdb->nQuery($qry_img, "image list error");
|
||||
// 이미지 가져오기
|
||||
$qry_img = "
|
||||
SELECT *
|
||||
FROM tbl_customer_image tci
|
||||
LEFT OUTER JOIN tbl_member tm ON tci.i_createdby = tm.m_uid
|
||||
WHERE tci.i_customeruid = '$c_uid'
|
||||
AND tci.i_status = 'A'
|
||||
ORDER BY tci.i_uid DESC
|
||||
";
|
||||
$rt_img = $jdb->nQuery($qry_img, "image list error");
|
||||
|
||||
// 분류 배열
|
||||
$install = [];
|
||||
$container = [];
|
||||
function getImageTypeLabel($type) {
|
||||
switch ($type) {
|
||||
case 'install':
|
||||
return 'Install Image';
|
||||
case 'install_order':
|
||||
return 'Container Request Image';
|
||||
case 'clean_before':
|
||||
return 'Before Clean';
|
||||
case 'clean_after':
|
||||
return 'After Clean';
|
||||
default:
|
||||
return ucfirst(str_replace('_', ' ', $type));
|
||||
}
|
||||
}
|
||||
|
||||
while ($img = mysqli_fetch_array($rt_img, MYSQLI_ASSOC)) {
|
||||
if ($img['i_type'] == "install") $install[] = $img;
|
||||
else $container[] = $img;
|
||||
}
|
||||
|
||||
if (count($install) + count($container) == 0) {
|
||||
if (mysqli_num_rows($rt_img) == 0) {
|
||||
echo "<p>No images found.</p>";
|
||||
} else {
|
||||
} else {
|
||||
echo '<div style="display:flex; flex-wrap:wrap; gap:16px;">';
|
||||
|
||||
// ===========================
|
||||
// INSTALL
|
||||
// ===========================
|
||||
if (count($install) > 0) {
|
||||
echo "<h4 style='margin-top:20px;'>Install</h4>";
|
||||
while ($img = mysqli_fetch_array($rt_img, MYSQLI_ASSOC)) {
|
||||
|
||||
foreach ($install as $img) {
|
||||
echo '<div style="
|
||||
width:220px;
|
||||
border:1px solid #ddd;
|
||||
border-radius:8px;
|
||||
padding:10px;
|
||||
background:#fafafa;
|
||||
position:relative;
|
||||
">';
|
||||
|
||||
echo '<div style="
|
||||
border:1px solid #ddd;
|
||||
padding:15px;
|
||||
border-radius:8px;
|
||||
margin-bottom:20px;
|
||||
background:#fafafa;
|
||||
">';
|
||||
echo '
|
||||
<button
|
||||
class="btn btn-sm btn-danger btn-delete-image"
|
||||
data-uid="'.htmlspecialchars($img['i_uid']).'"
|
||||
style="
|
||||
position:absolute;
|
||||
top:6px;
|
||||
right:6px;
|
||||
padding:2px 6px;
|
||||
font-size:11px;
|
||||
"
|
||||
title="Delete image"
|
||||
>
|
||||
<i class="bi bi-x"></i>
|
||||
</button>
|
||||
';
|
||||
|
||||
echo '<div style="display:flex; gap:15px; flex-wrap:wrap;">';
|
||||
// 이미지 영역 (위)
|
||||
echo '<div style="display:flex; gap:6px; flex-wrap:wrap; margin-bottom:8px;">';
|
||||
|
||||
$files = explode(",", $img['i_filename']);
|
||||
foreach ($files as $f) {
|
||||
$imgPath = $img['i_filepath'] . trim($f);
|
||||
echo '
|
||||
<div style="width:150px; text-align:center;">
|
||||
<img src="'.$imgPath.'"
|
||||
class="preview-img"
|
||||
data-img="'.$imgPath.'"
|
||||
data-filename="'.$f.'"
|
||||
data-uid="'.$img['i_uid'].'"
|
||||
data-customeruid="'.$c_uid.'"
|
||||
data-type="'.$img['i_type'].'"
|
||||
style="width:100%; height:120px;
|
||||
object-fit:cover; border-radius:5px;
|
||||
cursor:pointer; border:1px solid #ccc;">
|
||||
</div>
|
||||
';
|
||||
}
|
||||
$files = explode(",", $img['i_filename']);
|
||||
foreach ($files as $f) {
|
||||
$f = trim($f);
|
||||
if (!$f) continue;
|
||||
|
||||
echo '</div>';
|
||||
$imgPath = $img['i_filepath'] . $f;
|
||||
|
||||
echo '<div style="margin-top:10px; font-size:14px; line-height:20px;">
|
||||
<b>Note:</b> '.nl2br($img['i_note']).'<br>';
|
||||
echo "<b>Created:</b> " . date("Y-m-d H:i:s", strtotime($img['i_createddate'])).'<br>';
|
||||
echo "<b>Created by:</b> " . $img['m_firstname']. ' ' . $img['m_lastname']. ' (' . $img['m_initial']. ')';
|
||||
echo '</div></div>';
|
||||
}
|
||||
echo '<img
|
||||
src="'.htmlspecialchars($imgPath).'"
|
||||
class="preview-img"
|
||||
data-img="'.htmlspecialchars($imgPath).'"
|
||||
style="
|
||||
width:100px;
|
||||
height:80px;
|
||||
object-fit:cover;
|
||||
border-radius:5px;
|
||||
cursor:pointer;
|
||||
border:1px solid #ccc;
|
||||
"
|
||||
>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
// 설명 영역 (아래)
|
||||
echo '<div style="font-size:12px; line-height:1.4;">';
|
||||
echo '
|
||||
<div style="margin-bottom:5px;">
|
||||
<span class="badge bg-secondary" style="font-size:11px;">
|
||||
'.htmlspecialchars(getImageTypeLabel($img['i_type'])).'
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div><b>Note:</b> '.nl2br(htmlspecialchars($img['i_note'])).'</div>
|
||||
<div><b>Created:</b> '.date("Y-m-d H:i", strtotime($img['i_createddate'])).'</div>
|
||||
<div><b>By:</b> '
|
||||
. htmlspecialchars($img['m_firstname'] ?? '')
|
||||
. ' '
|
||||
. htmlspecialchars($img['m_lastname'] ?? '')
|
||||
.'</div>
|
||||
';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
echo '</div>'; // card end
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// CONTAINER
|
||||
// ===========================
|
||||
// if (count($container) > 0) {
|
||||
// echo "<h4 style='margin-top:30px;'>Container</h4>";
|
||||
|
||||
// foreach ($container as $img) {
|
||||
|
||||
// echo '<div style="
|
||||
// border:1px solid #ddd;
|
||||
// padding:15px;
|
||||
// border-radius:8px;
|
||||
// margin-bottom:20px;
|
||||
// background:#fafafa;
|
||||
// ">';
|
||||
|
||||
// echo '<div style="display:flex; gap:15px; flex-wrap:wrap;">';
|
||||
|
||||
// $files = explode(",", $img['i_filename']);
|
||||
// foreach ($files as $f) {
|
||||
// $imgPath = $img['i_filepath'] . trim($f);
|
||||
// echo '
|
||||
// <div style="width:150px; text-align:center;">
|
||||
// <img src="'.$imgPath.'"
|
||||
// class="preview-img"
|
||||
// data-img="'.$imgPath.'"
|
||||
// data-filename="'.$f.'"
|
||||
// data-uid="'.$img['i_uid'].'"
|
||||
// data-customeruid="'.$c_uid.'"
|
||||
// data-type="'.$img['i_type'].'"
|
||||
// style="width:100%; height:120px;
|
||||
// object-fit:cover; border-radius:5px;
|
||||
// cursor:pointer; border:1px solid #ccc;">
|
||||
// </div>
|
||||
// ';
|
||||
// }
|
||||
|
||||
// echo '</div>';
|
||||
|
||||
// echo '<div style="margin-top:10px; font-size:14px; line-height:20px;">
|
||||
// <b>Note:</b> '.nl2br($img['i_note']).'<br>';
|
||||
// echo "<b>Created:</b> " . date("Y-m-d H:i:s", strtotime($img['i_createddate'])).'<br>';
|
||||
// echo '
|
||||
// </div>
|
||||
// <button onclick="openEditModal(
|
||||
// \''.$img['i_uid'].'\',
|
||||
// \''.htmlspecialchars($img['i_note'], ENT_QUOTES).'\',
|
||||
// \''.$c_uid.'\',
|
||||
// \''.htmlspecialchars($img['i_filename'], ENT_QUOTES).'\',
|
||||
// \''.$img['i_filepath'].'\',
|
||||
// \''.$img['i_type'].'\'
|
||||
// )"
|
||||
// style="margin-top:10px; padding:4px 10px; background:#007bff; color:white; border:none; border-radius:4px;">
|
||||
// Edit
|
||||
// </button>';
|
||||
|
||||
// echo '</div>';
|
||||
// }
|
||||
// }
|
||||
}
|
||||
echo '</div>'; // wrapper end
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
|
|
@ -2211,7 +2190,7 @@ if( $total_count < 1 ) {
|
|||
<form id="deleteImageForm" method="POST" action="/lib/user_process.php">
|
||||
<input type="hidden" name="actionStr" value="DELETEIMAGE">
|
||||
<input type="hidden" name="i_uid" id="delete_i_uid">
|
||||
<input type="hidden" name="i_customeruid" id="delete_i_customeruid">
|
||||
<input type="hidden" name="i_customeruid" id="delete_i_customeruid" value="<?=$c_uid?>">
|
||||
<input type="hidden" name="fileName" id="delete_fileName">
|
||||
<input type="hidden" name="goStr" value="<?=$goStr?>">
|
||||
</form>
|
||||
|
|
@ -2414,22 +2393,48 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
// Delete image
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
const deleteBtn = document.getElementById("btnDeleteImage");
|
||||
const confirmModalObj = new bootstrap.Modal(document.getElementById("deleteConfirmModal"));
|
||||
const modalEl = document.getElementById("deleteConfirmModal");
|
||||
if (!modalEl) return;
|
||||
|
||||
if (deleteBtn) {
|
||||
deleteBtn.addEventListener("click", function () {
|
||||
confirmModalObj.show();
|
||||
});
|
||||
}
|
||||
const confirmModalObj = new bootstrap.Modal(modalEl);
|
||||
|
||||
document.getElementById("confirmDeleteBtn").addEventListener("click", function () {
|
||||
const uid = document.getElementById("currentImageUid").value;
|
||||
// 삭제 버튼 클릭
|
||||
document.addEventListener("click", function (e) {
|
||||
const btn = e.target.closest(".btn-delete-image");
|
||||
if (!btn) return;
|
||||
|
||||
const uid = btn.dataset.uid;
|
||||
if (!uid) return;
|
||||
|
||||
// 현재 선택된 이미지 UID 저장
|
||||
const uidInput = document.getElementById("currentImageUid");
|
||||
if (uidInput) {
|
||||
uidInput.value = uid;
|
||||
}
|
||||
|
||||
confirmModalObj.show();
|
||||
});
|
||||
|
||||
// confirm 버튼 클릭
|
||||
const confirmBtn = document.getElementById("confirmDeleteBtn");
|
||||
if (confirmBtn) {
|
||||
confirmBtn.addEventListener("click", function () {
|
||||
|
||||
const uid = document.getElementById("currentImageUid")?.value;
|
||||
if (!uid) return;
|
||||
|
||||
document.getElementById("delete_i_uid").value = uid;
|
||||
document.getElementById("deleteImageForm").submit();
|
||||
});
|
||||
const formInput = document.getElementById("delete_i_uid");
|
||||
if (formInput) {
|
||||
formInput.value = uid;
|
||||
}
|
||||
|
||||
const form = document.getElementById("deleteImageForm");
|
||||
if (form) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function openEditModal(uid, note, customeruid, filesString, filepath, type) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
$selectedDate = $_GET['date'] ?? date('Y-m-d');
|
||||
$canViewAllDrivers = ($_SESSION['ss_LEVEL'] <= 5 || $_SESSION['ss_GROUP'] == 1);
|
||||
$selectedDriver = $_GET['driver_uid'] ?? ($canViewAllDrivers ? 0 : $_SESSION['ss_DRUID']);
|
||||
$view_mode = strtolower($_GET['view_mode'] ?? 'list');
|
||||
|
||||
$isAdmin = $canViewAllDrivers;
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ $sql_order = "
|
|||
SELECT 1
|
||||
FROM tbl_customer_image ci
|
||||
WHERE ci.i_sourceuid = di.di_uid
|
||||
and ci.i_type in ('install','clean_before','clean_after')
|
||||
LIMIT 1
|
||||
) AS has_image
|
||||
FROM tbl_daily_install di
|
||||
|
|
@ -105,11 +107,10 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'work_images') {
|
|||
|
||||
// filepath + filename split
|
||||
$files = array_filter(array_map('trim', explode(',', $img['i_filename'] ?? '')));
|
||||
$first = $files ? $files[0] : '';
|
||||
|
||||
$imgPath = '';
|
||||
if ($first !== '') {
|
||||
$imgPath = ($img['i_filepath'] ?? '') . $first;
|
||||
$images = [];
|
||||
foreach ($files as $f) {
|
||||
$images[] = ($img['i_filepath'] ?? '') . $f;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
|
|
@ -120,8 +121,7 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'work_images') {
|
|||
'i_sourceuid' => $img['i_sourceuid'],
|
||||
'i_note' => $img['i_note'],
|
||||
'i_createddate' => $img['i_createddate'],
|
||||
'imgPath' => $imgPath,
|
||||
'filename' => $first
|
||||
'images' => $images // ⭐ 핵심
|
||||
]
|
||||
]);
|
||||
exit;
|
||||
|
|
@ -204,11 +204,11 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'work_images') {
|
|||
<!-- TOGGLE -->
|
||||
<div class="radio-segment d-flex" style="width:100%;">
|
||||
<label class="w-50 text-center">
|
||||
<input type="radio" name="view_mode" value="list" checked>
|
||||
<input type="radio" name="view_mode" value="list" <?= $view_mode === 'list' ? 'checked' : '' ?>>
|
||||
<span>LIST</span>
|
||||
</label>
|
||||
<label class="w-50 text-center">
|
||||
<input type="radio" name="view_mode" value="map">
|
||||
<input type="radio" name="view_mode" value="map" <?= $view_mode === 'map' ? 'checked' : '' ?>>
|
||||
<span>MAP</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -348,7 +348,7 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'work_images') {
|
|||
|
||||
<div id="wm_before_photo_list" class="photo-body"></div>
|
||||
|
||||
<input type="file" class="form-control form-control-sm mt-2" id="wm_photos_before" accept="image/*">
|
||||
<input type="file" class="form-control form-control-sm mt-2" id="wm_photos_before" accept="image/*" multiple>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -359,7 +359,7 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'work_images') {
|
|||
|
||||
<div id="wm_after_photo_list" class="photo-body"></div>
|
||||
|
||||
<input type="file" class="form-control form-control-sm mt-2" id="wm_photos_after" accept="image/*">
|
||||
<input type="file" class="form-control form-control-sm mt-2" id="wm_photos_after" accept="image/*" multiple>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -380,6 +380,7 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'work_images') {
|
|||
<!-- =============================== -->
|
||||
<!-- IMAGE MODAL -->
|
||||
<!-- =============================== -->
|
||||
<script src="/js/customer_image.js?v=<?=time()?>"></script>
|
||||
<div class="modal fade" id="customerImageModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
|
|
@ -669,20 +670,19 @@ function initWorkModal(){
|
|||
const workType = selectedRow.di_work_type;
|
||||
const afterFileInput = document.getElementById('wm_photos_after');
|
||||
const beforeFileInput = document.getElementById('wm_photos_before');
|
||||
const afterFile = afterFileInput?.files?.[0] || null;
|
||||
const beforeFile = beforeFileInput?.files?.[0] || null;
|
||||
const afterFiles = Array.from(afterFileInput?.files || []);
|
||||
const beforeFiles = Array.from(beforeFileInput?.files || []);
|
||||
|
||||
let uploads = [];
|
||||
|
||||
// AFTER
|
||||
if(afterFile){
|
||||
if(afterFiles.length){
|
||||
const type = (workType === 'CLEAN') ? 'clean_after' : 'install';
|
||||
uploads.push(uploadPhotoPromise(selectedRow, afterFile, type));
|
||||
uploads.push(uploadPhotoPromise(selectedRow, afterFiles, type));
|
||||
}
|
||||
|
||||
// BEFORE (CLEAN만)
|
||||
if(workType === 'CLEAN' && beforeFile){
|
||||
uploads.push(uploadPhotoPromise(selectedRow, beforeFile, 'clean_before'));
|
||||
if(workType === 'CLEAN' && beforeFiles.length){
|
||||
uploads.push(uploadPhotoPromise(selectedRow, beforeFiles, 'clean_before'));
|
||||
}
|
||||
|
||||
if(uploads.length > 0){
|
||||
|
|
@ -1053,26 +1053,24 @@ function renderAfterPhoto(photo){
|
|||
const wrap = document.getElementById('wm_after_photo_list');
|
||||
if(!wrap) return;
|
||||
|
||||
if(!photo || !photo.imgPath){
|
||||
if(!photo || !photo.images || !photo.images.length){
|
||||
wrap.innerHTML = `<div class="small text-muted">No after photo.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const url = photo.imgPath;
|
||||
|
||||
wrap.innerHTML = `
|
||||
<div style="width:100%;">
|
||||
<img src="${escapeHtml(url)}"
|
||||
style="width:100%; height:160px; object-fit:cover; border-radius:8px; border:1px solid #ddd; cursor:pointer;"
|
||||
class="after-photo-thumb"
|
||||
data-img-url="${escapeHtml(url)}">
|
||||
<div style="display:flex; gap:8px; flex-wrap:wrap;">
|
||||
${photo.images.map(url => `
|
||||
<img src="${escapeHtml(url)}"
|
||||
style="width:100px; height:80px; object-fit:cover; border-radius:6px; border:1px solid #ddd; cursor:pointer;"
|
||||
class="after-photo-thumb"
|
||||
data-img-url="${escapeHtml(url)}">
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 클릭 이벤트 연결
|
||||
const imgEl = wrap.querySelector('.after-photo-thumb');
|
||||
if(imgEl){
|
||||
imgEl.addEventListener('click', function(){
|
||||
wrap.querySelectorAll('.after-photo-thumb').forEach(img => {
|
||||
img.addEventListener('click', function(){
|
||||
|
||||
const modalEl = document.getElementById('customerImageModal');
|
||||
const body = document.getElementById('customerImageModalBody');
|
||||
|
|
@ -1081,15 +1079,14 @@ function renderAfterPhoto(photo){
|
|||
|
||||
body.innerHTML = `
|
||||
<div class="text-center">
|
||||
<img src="${escapeHtml(url)}"
|
||||
<img src="${this.dataset.imgUrl}"
|
||||
style="max-width:100%; max-height:75vh; border-radius:10px;">
|
||||
</div>
|
||||
`;
|
||||
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
modal.show();
|
||||
new bootstrap.Modal(modalEl).show();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderBeforePhoto(photo){
|
||||
|
|
@ -1097,19 +1094,40 @@ function renderBeforePhoto(photo){
|
|||
const wrap = document.getElementById('wm_before_photo_list');
|
||||
if(!wrap) return;
|
||||
|
||||
if(!photo || !photo.imgPath){
|
||||
if(!photo || !photo.images || !photo.images.length){
|
||||
wrap.innerHTML = `<div class="small text-muted">No before photo.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const url = photo.imgPath;
|
||||
|
||||
wrap.innerHTML = `
|
||||
<div style="width:100%;">
|
||||
<img src="${escapeHtml(url)}"
|
||||
style="width:100%; height:160px; object-fit:cover; border-radius:8px; border:1px solid #ddd; cursor:pointer;">
|
||||
<div style="display:flex; gap:8px; flex-wrap:wrap;">
|
||||
${photo.images.map(url => `
|
||||
<img src="${escapeHtml(url)}"
|
||||
style="width:100px; height:80px; object-fit:cover; border-radius:6px; border:1px solid #ddd; cursor:pointer;"
|
||||
class="before-photo-thumb"
|
||||
data-img-url="${escapeHtml(url)}">
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
wrap.querySelectorAll('.before-photo-thumb').forEach(img => {
|
||||
img.addEventListener('click', function(){
|
||||
|
||||
const modalEl = document.getElementById('customerImageModal');
|
||||
const body = document.getElementById('customerImageModalBody');
|
||||
|
||||
if(!modalEl || !body) return;
|
||||
|
||||
body.innerHTML = `
|
||||
<div class="text-center">
|
||||
<img src="${this.dataset.imgUrl}"
|
||||
style="max-width:100%; max-height:75vh; border-radius:10px;">
|
||||
</div>
|
||||
`;
|
||||
|
||||
new bootstrap.Modal(modalEl).show();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function resetPhotoBlocks(){
|
||||
|
|
@ -1159,6 +1177,7 @@ function openCustomerImages(customerUid, iType = 'install_order') {
|
|||
.then(res => res.text())
|
||||
.then(html => {
|
||||
body.innerHTML = html;
|
||||
initCustomerImageSlider(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
|
|
@ -1238,7 +1257,7 @@ function togglePhotoSection(workType){
|
|||
}
|
||||
}
|
||||
|
||||
function uploadPhotoPromise(row, file, type){
|
||||
function uploadPhotoPromise(row, files, type){
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
||||
|
|
@ -1250,7 +1269,9 @@ function uploadPhotoPromise(row, file, type){
|
|||
fd.append('member_uid', memberUid);
|
||||
fd.append('note', (document.getElementById('wm_comment').value || '').trim());
|
||||
fd.append('i_type', type);
|
||||
fd.append('photo', file);
|
||||
files.forEach(file => {
|
||||
fd.append('photos[]', file);
|
||||
});
|
||||
|
||||
fetch('/assets/internal_api.php?func=upload_after_photo', {
|
||||
method: 'POST',
|
||||
|
|
@ -1855,12 +1876,14 @@ function reloadPage(){
|
|||
|
||||
const date = document.getElementById('installDate').value;
|
||||
const driver = document.getElementById('driverSelect').value;
|
||||
const viewMode = document.querySelector('input[name="view_mode"]:checked')?.value || 'list';
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
|
||||
url.searchParams.set('view', 'install_order');
|
||||
url.searchParams.set('date', date);
|
||||
url.searchParams.set('driver_uid', driver);
|
||||
url.searchParams.set('view_mode', viewMode);
|
||||
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
|
|
@ -2115,6 +2138,22 @@ function showPopupMessage(message, type = 'success', duration = 1200) {
|
|||
/* ===============================
|
||||
TOGGLE
|
||||
=================================*/
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const viewMode = params.get('view_mode') || 'list';
|
||||
|
||||
const targetRadio = document.querySelector(`input[name="view_mode"][value="${viewMode}"]`);
|
||||
|
||||
if (targetRadio) {
|
||||
targetRadio.checked = true;
|
||||
|
||||
// change 강제 실행
|
||||
targetRadio.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
document.querySelectorAll('input[name="view_mode"]').forEach(radio => {
|
||||
radio.addEventListener('change', function () {
|
||||
document.getElementById('mapSection')
|
||||
|
|
|
|||
|
|
@ -1562,6 +1562,7 @@ function formatPhone($phone) {
|
|||
di_customer_name: w.iw_customer_name,
|
||||
di_address: w.iw_address,
|
||||
di_city: w.iw_city,
|
||||
di_postal: w.iw_postal,
|
||||
di_lat: w.iw_lat,
|
||||
di_lng: w.iw_lng,
|
||||
di_work_type: w.iw_work_type,
|
||||
|
|
@ -1576,7 +1577,7 @@ function formatPhone($phone) {
|
|||
|
||||
// lat/lng 없으면 여기서 geocode
|
||||
if (!row.di_lat || !row.di_lng) {
|
||||
const fullAddress = [w.iw_address, w.iw_city].filter(Boolean).join(', ');
|
||||
const fullAddress = [w.iw_address, w.iw_city, w.iw_postal].filter(Boolean).join(', ');
|
||||
|
||||
if (fullAddress) {
|
||||
geocoder.geocode({ address: fullAddress }, (results, status) => {
|
||||
|
|
|
|||
|
|
@ -107,11 +107,9 @@
|
|||
<!-- New -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>
|
||||
<script src="/js/customer_image.js?v=20260213"></script>
|
||||
|
||||
<div class="loading-overlay">Loading...</div>
|
||||
|
||||
<div class="loading-overlay">Loading...</div>
|
||||
|
||||
<div id="popupMessage"></div>
|
||||
|
||||
|
|
@ -799,6 +797,7 @@ function popup(){
|
|||
</div>
|
||||
|
||||
<!-- image modal -->
|
||||
<script src="/js/customer_image.js?v=<?=time()?>"></script>
|
||||
<div class="modal fade top map" id="customerImageModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
|
|
@ -2514,6 +2513,7 @@ function popup(){
|
|||
const outer = document.getElementById('simpleInstallPhotoWrap');
|
||||
|
||||
if (!wrap || !outer) return;
|
||||
|
||||
wrap.innerHTML = '';
|
||||
|
||||
if (!photos || photos.length === 0) {
|
||||
|
|
@ -2522,20 +2522,35 @@ function popup(){
|
|||
}
|
||||
|
||||
outer.style.display = 'block';
|
||||
|
||||
photos.forEach(photo => {
|
||||
|
||||
if (!photo.imgPath) return;
|
||||
if (!photo.i_filename) return;
|
||||
|
||||
const url = photo.imgPath;
|
||||
const div = document.createElement('div');
|
||||
div.style.width = '120px';
|
||||
const basePath = (photo.i_filepath || '').trim();
|
||||
|
||||
div.innerHTML = `
|
||||
<img src="${url}"
|
||||
const filenames = photo.i_filename
|
||||
.split(',')
|
||||
.map(f => f.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
filenames.forEach(name => {
|
||||
|
||||
const fullUrl = basePath.endsWith('/')
|
||||
? basePath + name
|
||||
: basePath + '/' + name;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.style.width = '120px';
|
||||
|
||||
div.innerHTML = `
|
||||
<img src="${fullUrl}"
|
||||
style="width:100%; height:100px; object-fit:cover; border-radius:8px; border:1px solid #ddd;">
|
||||
`;
|
||||
`;
|
||||
|
||||
wrap.appendChild(div);
|
||||
});
|
||||
|
||||
wrap.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2784,6 +2799,7 @@ function popup(){
|
|||
.then(res => res.text())
|
||||
.then(html => {
|
||||
body.innerHTML = html;
|
||||
initCustomerImageSlider(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ $sqDriver = qry("SELECT
|
|||
FROM
|
||||
tbl_member
|
||||
WHERE
|
||||
m_level = 9 AND m_status = 'A' ".$qrySTR."
|
||||
m_level in (8,9) AND m_status = 'A' ".$qrySTR." AND m_gid = 0
|
||||
ORDER BY
|
||||
m_initial ASC ");
|
||||
while($rstDriver = fetch_array($sqDriver)){
|
||||
|
|
@ -56,12 +56,10 @@ while($rstDriver = fetch_array($sqDriver)){
|
|||
|
||||
<label>Created By</label>
|
||||
<select name="i_createdby" id="driver" class="custom-select-map-orderby" required>
|
||||
<option value="">Select</option>
|
||||
<option value="<?=$_SESSION['ss_UID']?>">Select</option>
|
||||
<?php
|
||||
$defaultValue = 43; // E.X
|
||||
foreach($drivers as $driver) {
|
||||
$selected = ($driver['id'] == $defaultValue) ? 'selected' : '';
|
||||
echo '<option value="'.$driver['id'].'" '.$selected.'>'.$driver['name'].'</option>';
|
||||
echo '<option value="'.$driver['id'].'">'.$driver['name'].'</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
|
|
@ -70,15 +68,17 @@ while($rstDriver = fetch_array($sqDriver)){
|
|||
<label>Type</label>
|
||||
<select name="i_type" id="i_type" required>
|
||||
<option value="install">Install</option>
|
||||
<option value="install_order">Install Request</option>
|
||||
<option value="clean_before">Before Clean</option>
|
||||
<option value="clean_after">After Clean</option>
|
||||
<!-- <option value="container">Container</option> -->
|
||||
</select>
|
||||
|
||||
<label>Image</label>
|
||||
<p style="font-size:12px; color:#666; margin-top:4px;">※ 기존 이미지가 있을 경우 삭제됩니다.</p>
|
||||
<p style="font-size:12px; color:#666; margin-top:-6px;">※ Install 이미지는 1개만 업로드 가능합니다.</p>
|
||||
<input type="file" id="uploadFileInput" name="upload_file[]" accept="image/*" required>
|
||||
<p style="font-size:12px; color:#666; margin-top:4px;">※ Will replace the same type images</p>
|
||||
<input type="file" id="uploadFileInput" name="upload_file[]" accept="image/*" multiple required>
|
||||
|
||||
<img id="previewImg" class="preview-img">
|
||||
<div id="previewList" style="display:flex; gap:8px; flex-wrap:wrap; margin-top:12px;"></div>
|
||||
|
||||
<label>Note</label>
|
||||
<textarea name="i_note" rows="3"></textarea>
|
||||
|
|
@ -105,19 +105,28 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
|
||||
// 이미지 preview
|
||||
const input = document.getElementById("uploadFileInput");
|
||||
const preview = document.getElementById("previewImg");
|
||||
const previewList = document.getElementById("previewList");
|
||||
|
||||
if (input) {
|
||||
input.addEventListener("change", function () {
|
||||
const file = this.files[0];
|
||||
if (!file) return;
|
||||
previewList.innerHTML = "";
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
preview.src = reader.result;
|
||||
preview.style.display = "block";
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
Array.from(this.files).forEach(file => {
|
||||
if (!file.type.startsWith("image/")) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
const img = document.createElement("img");
|
||||
img.src = reader.result;
|
||||
img.style.width = "90px";
|
||||
img.style.height = "70px";
|
||||
img.style.objectFit = "cover";
|
||||
img.style.border = "1px solid #ccc";
|
||||
img.style.borderRadius = "6px";
|
||||
previewList.appendChild(img);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ if ($actionStr == "DELETENOTE" && $mode == "delete") {
|
|||
// ADD IMAGE (Customer Detail - Image Upload)
|
||||
//////////////////////////////////////////////
|
||||
if ($actionStr == "ADDIMAGE") {
|
||||
$singleTypes = ['install', 'container', 'install_order'];
|
||||
$singleTypes = ['install', 'install_order', 'clean_before', 'clean_after'];
|
||||
|
||||
$i_note = isset($_POST['i_note']) ? trim($_POST['i_note']) : "";
|
||||
$i_createddate = date("YmdHis");
|
||||
|
|
@ -478,37 +478,6 @@ if ($actionStr == "ADDIMAGE") {
|
|||
$jdb->nQuery($qry_update, "update error");
|
||||
}
|
||||
|
||||
// 이미지 최대 6개 제한 체크
|
||||
if ($i_type == "container") {
|
||||
// 기존 이미지 수 조회
|
||||
$qry_cnt = "
|
||||
SELECT i_filename
|
||||
FROM tbl_customer_image
|
||||
WHERE i_customeruid = '$i_customeruid'
|
||||
AND i_type = 'container'
|
||||
AND i_status = 'A'
|
||||
ORDER BY i_uid DESC LIMIT 1
|
||||
";
|
||||
$rt_cnt = $jdb->fQuery($qry_cnt, "error");
|
||||
|
||||
$existingCount = 0;
|
||||
if (!empty($rt_cnt['i_filename'])) {
|
||||
$existingFiles = explode(",", $rt_cnt['i_filename']);
|
||||
$existingCount = count($existingFiles);
|
||||
}
|
||||
|
||||
// 이번에 업로드할 파일 개수
|
||||
$newCount = is_array($_FILES['upload_file']['name'])
|
||||
? count($_FILES['upload_file']['name'])
|
||||
: 1;
|
||||
|
||||
if ($existingCount + $newCount > 6) {
|
||||
$msg = "Container 이미지는 최대 6개까지 업로드할 수 있습니다.";
|
||||
$func->modalMsg($msg, "/index_intranet.php?view=customer_detail&mode=update&c_uid=$i_customeruid&$goStr");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// 업로드 파일 체크
|
||||
if (!isset($_FILES['upload_file'])) {
|
||||
$msg = "Upload failed. Please try again.";
|
||||
|
|
@ -592,7 +561,7 @@ if ($actionStr == "ADDIMAGE") {
|
|||
$values = array(
|
||||
$i_customeruid,
|
||||
$i_memberuid,
|
||||
$i_memberuid,
|
||||
$i_createdby,
|
||||
$i_createddate,
|
||||
$i_type,
|
||||
$fileNameString,
|
||||
|
|
|
|||
Loading…
Reference in New Issue