feature/template #5
@@ -73,11 +73,15 @@ $routes->group('article', ['namespace' => 'App\Controllers\Article'], function (
|
||||
$routes->post('apt/savePhoReason', 'Apt::savePhoReason');
|
||||
$routes->post('apt/saveCate', 'Apt::saveCate');
|
||||
$routes->post('apt/savePhotoView', 'Apt::savePhotoView');
|
||||
$routes->post('apt/savePhotoView', 'Apt::savePhotoView');
|
||||
$routes->post('apt/removePhoto', 'Apt::removePhoto');
|
||||
$routes->post('apt/confirmAptInfo', 'Apt::confirmAptInfo');
|
||||
$routes->post('apt/resendAptInfo', 'Apt::resendAptInfo');
|
||||
$routes->post('apt/savePhoExplain', 'Apt::savePhoExplain');
|
||||
$routes->post('apt/saveWriteComplete', 'Apt::saveWriteComplete');
|
||||
|
||||
$routes->post('apt/uploadFile', 'Apt::uploadFile');
|
||||
$routes->post('apt/savePhoCate', 'Apt::savePhoCate');
|
||||
$routes->post('apt/reqRemovePho', 'Apt::reqRemovePho');
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Controllers\BaseController;
|
||||
use App\Libraries\MyUpload;
|
||||
use App\Models\article\AptModel;
|
||||
use App\Models\common\CodeModel;
|
||||
use MY_Upload;
|
||||
|
||||
class Apt extends BaseController
|
||||
{
|
||||
@@ -673,6 +672,34 @@ class Apt extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
// 단지정보 작성완료
|
||||
public function saveWriteComplete()
|
||||
{
|
||||
try {
|
||||
|
||||
$rcpt_no = $this->request->getPost('rcpt_no');
|
||||
if (empty($rcpt_no)) {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => '정보누락'
|
||||
]);
|
||||
}
|
||||
|
||||
$this->aptModel->saveWriteComplete($rcpt_no);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'code' => '0',
|
||||
'msg' => 'success'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 검수완료 저장
|
||||
public function confirmAptInfo()
|
||||
{
|
||||
@@ -868,11 +895,7 @@ class Apt extends BaseController
|
||||
$rcpt_no = $this->request->getPost('rcpt_no');
|
||||
$uploadType = $this->request->getPost('upload_type');
|
||||
$files = $this->request->getFiles();
|
||||
$imgPath = "/upload/apt_file/" . $rcpt_no . "/";
|
||||
|
||||
$imgPath = NCLOUD_OBJECT_STORAGE_URL . $imgPath;
|
||||
$moviePath = $imgPath;
|
||||
$photo360Path = $imgPath;
|
||||
$uploadPath = "/upload/apt_file/" . $rcpt_no . "/";
|
||||
|
||||
if (!isset($files['files'])) {
|
||||
return $this->response->setJSON([
|
||||
@@ -881,36 +904,267 @@ class Apt extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
if ($uploadType === "photo") {
|
||||
|
||||
$arrUploadfile = [];
|
||||
foreach ($files['files'] as $file) {
|
||||
|
||||
// foreach ($files['files'] as $file) {
|
||||
// if ($file->isValid()) {
|
||||
// $file->move(WRITEPATH . 'uploads');
|
||||
$uploadData = $lib->do_upload2($file, $uploadPath);
|
||||
|
||||
// INSERT apt_photo
|
||||
if ($uploadData !== false) {
|
||||
$arrUploadfile[] = $uploadData;
|
||||
}
|
||||
|
||||
print_r($_FILES);
|
||||
// }
|
||||
}
|
||||
|
||||
$gps_lat = null;
|
||||
$gps_lon = null;
|
||||
$camDate = null;
|
||||
if (!empty($arrUploadfile)) {
|
||||
foreach ($arrUploadfile as $key => $uploadFile) {
|
||||
$object_storage_url = $uploadFile['object_storage_url'];
|
||||
$arrExifData = @exif_read_data($object_storage_url);
|
||||
if (!empty($arrExifData)) {
|
||||
$notFound = "Unavailable";
|
||||
if (@array_key_exists('DateTime', $arrExifData)) {
|
||||
$camDate = $arrExifData['DateTime'];
|
||||
} else {
|
||||
$camDate = $notFound;
|
||||
}
|
||||
|
||||
$imageMetaData = $camDate;
|
||||
$camDate = substr(str_replace(':', '-', $camDate), 0, 10);
|
||||
|
||||
|
||||
|
||||
// $imageDataBlob = file_get_contents($object_storage_url);
|
||||
// $im = new Imagick();
|
||||
// $im->readImageBlob($imageDataBlob);
|
||||
// $im->thumbnailImage(105, 80, false);
|
||||
// $thumb_im = $im->getImageBlob();
|
||||
// $lib->upload_object_storage_imagick();
|
||||
$arrGPS = $arrExifData['GPS'] ?? null;
|
||||
|
||||
// }
|
||||
if (empty($arrGPS)) { // GPS 섹션이 없으면, 개별 키로도 체크
|
||||
if (!empty($arrExifData['GPSLongitude']) && !empty($arrExifData['GPSLatitude'])) {
|
||||
$arrGPS = [
|
||||
'GPSLongitude' => $arrExifData['GPSLongitude'],
|
||||
'GPSLatitude' => $arrExifData['GPSLatitude'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!empty($arrGPS)
|
||||
&& !empty($arrGPS['GPSLongitude'])
|
||||
&& !empty($arrGPS['GPSLatitude'])
|
||||
&& is_array($arrGPS['GPSLongitude'])
|
||||
&& is_array($arrGPS['GPSLatitude'])
|
||||
) { //GPS 정보가 있다면
|
||||
if (@array_key_exists('GPSLongitude', $arrGPS) && (@array_key_exists('GPSLatitude', $arrGPS))) {
|
||||
list($temp_d1, $temp_d2) = sscanf($arrGPS["GPSLatitude"][0], "%d/%d"); //문자->숫자로 계산
|
||||
$gps_lat_d = $temp_d1 / $temp_d2;
|
||||
list($temp_d1, $temp_d2) = sscanf($arrGPS["GPSLatitude"][1], "%d/%d");
|
||||
$gps_lat_m = $temp_d1 / $temp_d2;
|
||||
list($temp_d1, $temp_d2) = sscanf($arrGPS["GPSLatitude"][2], "%d/%d");
|
||||
$gps_lat_s = $temp_d1 / $temp_d2;
|
||||
|
||||
list($temp_d1, $temp_d2) = sscanf($arrGPS["GPSLongitude"][0], "%d/%d"); //문자->숫자로 계산
|
||||
$gps_lon_d = $temp_d1 / $temp_d2;
|
||||
list($temp_d1, $temp_d2) = sscanf($arrGPS["GPSLongitude"][1], "%d/%d");
|
||||
$gps_lon_m = $temp_d1 / $temp_d2;
|
||||
list($temp_d1, $temp_d2) = sscanf($arrGPS["GPSLongitude"][2], "%d/%d");
|
||||
$gps_lon_s = $temp_d1 / $temp_d2;
|
||||
|
||||
$gps_lat = $gps_lat_d + $gps_lat_m / 60 + $gps_lat_s / 3600; //도분초를 도로 변환
|
||||
$gps_lon = $gps_lon_d + $gps_lon_m / 60 + $gps_lon_s / 3600;
|
||||
}
|
||||
} else {
|
||||
$xy = $this->aptModel->getDetail($rcpt_no);
|
||||
|
||||
$gps_lat = $xy['rcpt_y'];
|
||||
$gps_lon = $xy['rcpt_x'];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$base = $uploadFile['base_name']; // xxxx
|
||||
$dir = rtrim(dirname($uploadFile['object_key']), '/'); // upload/apt_file/2
|
||||
$thumbKey = $dir . '/' . $base . '_thumb.jpg';
|
||||
|
||||
$imageDataBlob = file_get_contents($object_storage_url);
|
||||
$im = new \Imagick();
|
||||
$im->readImageBlob($imageDataBlob);
|
||||
$im->thumbnailImage(105, 80, false);
|
||||
$thumb_im = $im->getImageBlob();
|
||||
// 썸네일 s3 전송
|
||||
$lib->upload_object_storage_imagick2($thumbKey, $thumb_im);
|
||||
|
||||
|
||||
/**
|
||||
* 파일업로드 내용 저장
|
||||
* rcpt_no, pho_lati, pho_long, filenm, filenm_up, file_path, thumb_path, thumb_nm, cloud_upload_yn
|
||||
*
|
||||
*/
|
||||
$uploadParam = [
|
||||
'rcpt_no' => $rcpt_no, // 접수번호
|
||||
'gps_lat' => $gps_lat, // latitude
|
||||
'gps_lon' => $gps_lon, // longitude
|
||||
'origin_name' => $uploadFile['origin_name'], // 원본파일명
|
||||
'file_name' => $uploadFile['file_name'], // 저장파일명
|
||||
'upload_path' => $uploadPath, // 저장경로
|
||||
'thumb_name' => $base . '_thumb.jpg',
|
||||
'cam_date' => $camDate, // 촬영일
|
||||
];
|
||||
|
||||
$res = $this->aptModel->saveImg($uploadParam);
|
||||
log_message('debug', 'apt_file :: rcpt_no : ' . $rcpt_no . ', fileName : ' . $uploadFile['file_name']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($uploadType === "video") {
|
||||
|
||||
|
||||
$arrUploadfile = [];
|
||||
foreach ($files['files'] as $file) {
|
||||
|
||||
$uploadData = $lib->do_upload2($file, $uploadPath);
|
||||
|
||||
if ($uploadData !== false) {
|
||||
$arrUploadfile[] = $uploadData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// print_r($arrUploadfile);
|
||||
// exit;
|
||||
|
||||
if (!empty($arrUploadfile)) {
|
||||
foreach ($arrUploadfile as $key => $uploadFile) {
|
||||
$uploadParam = [
|
||||
'rcpt_no' => $rcpt_no, // 접수번호
|
||||
'origin_name' => $uploadFile['origin_name'], // 원본파일명
|
||||
'file_name' => $uploadFile['file_name'], // 저장파일명
|
||||
'upload_path' => $uploadPath, // 저장경로
|
||||
// 'thumb_name' => $base . '_thumb.jpg',
|
||||
// 'cam_date' => $camDate, // 촬영일
|
||||
];
|
||||
|
||||
|
||||
// 동영상 정보 저장
|
||||
$this->aptModel->saveVideo($uploadParam);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $this->response->setJSON([
|
||||
'code' => '0',
|
||||
'msg' => 'success'
|
||||
]);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 업로드파일삭제
|
||||
public function reqRemovePho()
|
||||
{
|
||||
try {
|
||||
|
||||
$type = $this->request->getPost('type');
|
||||
|
||||
|
||||
if ($type === "all") {
|
||||
$rcpt_no = $this->request->getPost('rcpt_no');
|
||||
|
||||
// 사진 일괄 삭제
|
||||
$this->aptModel->removeAllPho($rcpt_no);
|
||||
|
||||
|
||||
} else if ($type === "select") {
|
||||
$phoNo = $this->request->getPost('phoNo'); // ✅ 배열로 들어옴
|
||||
|
||||
if (!is_array($phoNo))
|
||||
$phoNo = [$phoNo];
|
||||
|
||||
if (empty($phoNo)) {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => '데이터 누락'
|
||||
]);
|
||||
}
|
||||
|
||||
// 선택 사진 삭제
|
||||
$this->aptModel->removePho($phoNo);
|
||||
|
||||
|
||||
} else {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => '잘못된 접근'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
return $this->response->setJSON([
|
||||
'code' => '0',
|
||||
'msg' => 'success'
|
||||
]);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 업로드 파일 카테고리 지정
|
||||
public function savePhoCate()
|
||||
{
|
||||
try {
|
||||
|
||||
$rcpt_no = $this->request->getPost('rcpt_no');
|
||||
$code1 = $this->request->getPost('code1');
|
||||
$code2 = $this->request->getPost('code2');
|
||||
$phoNo = $this->request->getPost('phoNo');
|
||||
|
||||
if (!is_array($phoNo))
|
||||
$phoNo = [$phoNo];
|
||||
|
||||
if (empty($phoNo)) {
|
||||
return $this->response->setJSON([
|
||||
'code' => '9',
|
||||
'msg' => '데이터 누락'
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($phoNo as $pho) {
|
||||
|
||||
$data = [
|
||||
'rcpt_no' => $rcpt_no,
|
||||
'pho_no' => $pho,
|
||||
'code1' => $code1,
|
||||
'code2' => $code2,
|
||||
];
|
||||
|
||||
// 카테고리 지정
|
||||
$this->aptModel->updatePhoCate($data);
|
||||
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'code' => '0',
|
||||
'msg' => 'success'
|
||||
]);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->response->setJSON([
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
namespace App\Libraries;
|
||||
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\Exception\AwsException;
|
||||
use Aws\Credentials\CredentialProvider;
|
||||
use Aws\Credentials\Credentials;
|
||||
use CodeIgniter\HTTP\Files\UploadedFile;
|
||||
|
||||
class MyUpload
|
||||
{
|
||||
@@ -49,12 +53,76 @@ class MyUpload
|
||||
return $this;
|
||||
}
|
||||
|
||||
// CI3 스타일: do_upload()
|
||||
/**
|
||||
* 파일 업로드 요청
|
||||
* 추가일 2025.12.24
|
||||
* 작성자 - yangsh
|
||||
*/
|
||||
public function do_upload2(UploadedFile $file, $filePath = null): array|false
|
||||
{
|
||||
if (!$file->isValid()) {
|
||||
$this->set_error('upload_invalid_file');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 업로드 전인데 hasMoved()가 true면 비정상 상태
|
||||
if ($file->hasMoved()) {
|
||||
$this->set_error('upload_file_already_moved');
|
||||
return false;
|
||||
}
|
||||
|
||||
$newName = $file->getRandomName();
|
||||
|
||||
// ✅ PHP 임시 업로드 파일 경로 (writable로 move() 필요 없음)
|
||||
$tmpFile = $file->getTempName();
|
||||
if (!is_file($tmpFile)) {
|
||||
$this->set_error('upload_temp_file_missing');
|
||||
log_message('error', 'do_upload2 temp file missing: ' . $tmpFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ✅ 클라우드에 올라갈 "Key"를 직접 만든다 (로컬 경로 절대 넣지 말기)
|
||||
// 예시: upload/tmp/랜덤파일명 또는 upload/apt_file/{rcpt_no}/...
|
||||
$objectKey = $filePath . $newName;
|
||||
|
||||
$up = $this->upload_object_storage($objectKey, $tmpFile, 'file');
|
||||
if ($up === false) {
|
||||
$this->set_error('upload_destination_cloud_error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// (선택) tmp 파일 삭제
|
||||
@unlink($tmpFile);
|
||||
|
||||
$this->s3_data = [
|
||||
'object_key' => $objectKey,
|
||||
'object_storage_url' => $up['object_storage_url'] ?? null,
|
||||
'origin_name' => $file->getClientName(),
|
||||
'file_name' => basename($objectKey), // xxxx.jpg
|
||||
'base_name' => pathinfo($objectKey, PATHINFO_FILENAME), // xxxx
|
||||
'ext' => pathinfo($objectKey, PATHINFO_EXTENSION), // jpg
|
||||
];
|
||||
|
||||
|
||||
log_message('debug', 's3_data=' . json_encode($this->s3_data ?? null, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
|
||||
|
||||
return $this->s3_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* S3(NCLOUD) 파일 업로드
|
||||
* 추가일 2025.12.24
|
||||
* 작성자 - yangsh
|
||||
*/
|
||||
public function do_upload(string $field = 'userfile'): bool
|
||||
{
|
||||
|
||||
$request = service('request');
|
||||
$file = $request->getFile($field);
|
||||
|
||||
var_dump($file);
|
||||
|
||||
if (!$file) {
|
||||
$this->set_error('upload_no_file_selected');
|
||||
return false;
|
||||
@@ -222,10 +290,13 @@ class MyUpload
|
||||
$this->errors[] = $msg;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// === 너의 기존 S3 메서드들 (CI4용으로 client 생성만 보강) ===
|
||||
|
||||
public function upload_object_storage(string $key, string $temp_file, string $type = 'file'): bool
|
||||
/**
|
||||
* S3(NCLOUD) 파일 업로드
|
||||
* 수정일 2025.12.24
|
||||
* 작성자 - yangsh
|
||||
*/
|
||||
public function upload_object_storage(string $key, string $temp_file, string $type = 'file'): array|false
|
||||
{
|
||||
// CI3 코드의 경로 치환 로직 유지 (FCPATH는 CI4에도 존재)
|
||||
$object_storage_upload_path = str_replace(FCPATH, '/', $key);
|
||||
@@ -236,27 +307,77 @@ class MyUpload
|
||||
$s3Client = $this->makeS3Client();
|
||||
|
||||
try {
|
||||
$body = file_get_contents($temp_file);
|
||||
// $body = file_get_contents($temp_file);
|
||||
|
||||
// $response = $s3Client->putObject([
|
||||
// 'Bucket' => NCLOUD_S3_BUCKET,
|
||||
// 'Key' => ltrim($object_storage_upload_path, '/'),
|
||||
// 'Body' => $body,
|
||||
// 'ACL' => 'public-read',
|
||||
// ]);
|
||||
|
||||
$response = $s3Client->putObject([
|
||||
'Bucket' => NCLOUD_S3_BUCKET,
|
||||
'Key' => ltrim($object_storage_upload_path, '/'),
|
||||
'Body' => $body,
|
||||
'SourceFile' => $temp_file, // ✅ 동영상도 OK
|
||||
'ACL' => 'public-read',
|
||||
|
||||
// (선택) 타입별 ContentType 지정 (브라우저 재생/다운로드에 중요)
|
||||
// 'ContentType' => $this->guessMime($temp_file, $type),
|
||||
]);
|
||||
|
||||
$this->s3_data = [
|
||||
'object_storage_upload_path' => $object_storage_upload_path,
|
||||
// $this->s3_data = [
|
||||
// 'object_storage_upload_path' => $object_storage_upload_path,
|
||||
// 'object_storage_url' => $response['ObjectURL'] ?? null,
|
||||
// ];
|
||||
|
||||
return [
|
||||
'object_storage_url' => $response['ObjectURL'] ?? null,
|
||||
];
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
// 운영에서는 echo 지양. 로그로 남기는 걸 추천
|
||||
// log_message('error', $e->getMessage());
|
||||
log_message('error', '[S3 UPLOAD FAIL] ' . $e->getMessage());
|
||||
log_message('error', '[S3 UPLOAD TRACE] ' . $e->getTraceAsString());
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* S3(NCLOUD) 파일 업로드 - 썸네일
|
||||
* 추가일 2025.12.24
|
||||
* 작성자 - yangsh
|
||||
*/
|
||||
public function upload_object_storage_imagick2($key, $blobData): bool
|
||||
{
|
||||
$object_storage_upload_path = str_replace(FCPATH, '/', $key);
|
||||
$object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path);
|
||||
$object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path);
|
||||
$object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path);
|
||||
|
||||
$s3Client = $this->makeS3Client();
|
||||
|
||||
try {
|
||||
|
||||
$response = $s3Client->putObject([
|
||||
'Bucket' => NCLOUD_S3_BUCKET,
|
||||
'Key' => ltrim($object_storage_upload_path, '/'),
|
||||
'Body' => $blobData,
|
||||
'ACL' => 'public-read',
|
||||
]);
|
||||
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
// 운영에서는 echo 지양. 로그로 남기는 걸 추천
|
||||
log_message('error', '[S3 UPLOAD FAIL] ' . $e->getMessage());
|
||||
log_message('error', '[S3 UPLOAD TRACE] ' . $e->getTraceAsString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public function upload_object_storage_imagick(string $key, $blobData): bool
|
||||
@@ -306,28 +427,18 @@ class MyUpload
|
||||
|
||||
protected function makeS3Client(): S3Client
|
||||
{
|
||||
// AWS SDK v2: S3Client::factory / v3+: new S3Client
|
||||
if (method_exists(S3Client::class, 'factory')) {
|
||||
/** @noinspection PhpUndefinedMethodInspection */
|
||||
return S3Client::factory([
|
||||
'key' => NCLOUD_S3_KEY,
|
||||
'secret' => NCLOUD_S3_SECRET,
|
||||
'endpoint' => NCLOUD_S3_ENDPOINT,
|
||||
'debug' => true,
|
||||
'ssl.certificate_authority' => false,
|
||||
]);
|
||||
}
|
||||
$region = defined('NCLOUD_S3_REGION') ? NCLOUD_S3_REGION : 'ap-northeast-2';
|
||||
|
||||
// ✅ credentials를 provider로 강제하면 provider-chain(IMDS) 안 탐
|
||||
$creds = new Credentials(NCLOUD_S3_KEY, NCLOUD_S3_SECRET);
|
||||
$provider = CredentialProvider::fromCredentials($creds);
|
||||
|
||||
return new S3Client([
|
||||
'endpoint' => NCLOUD_S3_ENDPOINT,
|
||||
'credentials' => [
|
||||
'key' => NCLOUD_S3_KEY,
|
||||
'secret' => NCLOUD_S3_SECRET,
|
||||
],
|
||||
'region' => defined('NCLOUD_S3_REGION') ? NCLOUD_S3_REGION : 'kr-standard',
|
||||
'version' => 'latest',
|
||||
// (보안주의) 필요하면 verify 설정을 추가
|
||||
// 'http' => ['verify' => false],
|
||||
'region' => $region,
|
||||
'endpoint' => NCLOUD_S3_ENDPOINT,
|
||||
'credentials' => $provider,
|
||||
'use_path_style_endpoint' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -772,6 +772,7 @@ class AptModel extends Model
|
||||
,b.memo, b.note, b.video_target, b.vdo_up_ynx, b.not_vdo_reson, b.apt_step, b.not_vdo_tm, b.check_yn, b.resend_yn, b.write_complete_yn, b.all_no_pho
|
||||
,b.write_complete_tm, DATE_FORMAT(b.write_complete_tm, '%Y-%m-%d') as rdate_dt_cmpl ,DATE_FORMAT(b.write_complete_tm, '%H:%i:%s') as rdate_tm_cmpl
|
||||
,b.charger, b.dept_sq ,(SELECT pdept_sq FROM departments WHERE dept_sq = b.dept_sq) bonbu
|
||||
,IFNULL((SELECT CONCAT(file_path, '', filenm_up) FROM apt_photo WHERE rcpt_no = a.rcpt_no AND pho_cate1 = 'V' AND pho_cate2 = 'V01' ORDER BY pho_no DESC LIMIT 1), '') AS vdo_path
|
||||
FROM
|
||||
apt_receipt a
|
||||
JOIN apt_result b ON a.rcpt_no = b.rcpt_no
|
||||
@@ -1030,8 +1031,24 @@ class AptModel extends Model
|
||||
'pho_cate2' => $params['pho_cate2'],
|
||||
];
|
||||
|
||||
$this->db->where_in('pho_no', $params['pho_no']);
|
||||
$result = $this->db->update('apt_photo', $data);
|
||||
$phoNos = $params['pho_no'] ?? [];
|
||||
|
||||
if (!is_array($phoNos)) {
|
||||
$phoNos = [$phoNos];
|
||||
}
|
||||
|
||||
if (empty($phoNos)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => '대상 pho_no가 없습니다.',
|
||||
];
|
||||
}
|
||||
|
||||
$builder = $this->db->table('apt_photo');
|
||||
|
||||
$builder->whereIn('pho_no', $phoNos);
|
||||
$result = $builder->update($data);
|
||||
|
||||
|
||||
if ($result === false) {
|
||||
return [
|
||||
@@ -1182,6 +1199,31 @@ class AptModel extends Model
|
||||
];
|
||||
}
|
||||
|
||||
// 단지정보 작성완료
|
||||
public function saveWriteComplete($rcpt_no)
|
||||
{
|
||||
$sql = " UPDATE apt_result" .
|
||||
" SET write_complete_yn = 'Y'" .
|
||||
" ,apt_step = CASE WHEN vdo_up_ynx = 'N' THEN 'S02' ELSE 'S04' END" .
|
||||
" ,write_complete_tm = now()" .
|
||||
" WHERE rcpt_no = ? ";
|
||||
|
||||
if ($this->db->query($sql, [$rcpt_no])) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => '저장실패',
|
||||
];
|
||||
}
|
||||
|
||||
$now = $this->getDetail($rcpt_no);
|
||||
$this->saveHistory($rcpt_no, $now['apt_step'], 'C', 'C1', session('usr_id'));
|
||||
|
||||
// 성공
|
||||
return [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// 단지실사 API 정보
|
||||
public function new_api_photo_send_data($rcpt_no)
|
||||
@@ -1246,4 +1288,153 @@ class AptModel extends Model
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// 업로드 파일정보 저장
|
||||
public function saveImg($params)
|
||||
{
|
||||
$sql = "INSERT INTO apt_photo
|
||||
(rcpt_no, pho_lati, pho_long, filenm, filenm_up, pho_view_yn, pho_date, insert_tm, file_path, use_yn, thumb_path, thumb_nm, cloud_upload_yn)
|
||||
VALUES
|
||||
(
|
||||
{$params['rcpt_no']},
|
||||
'{$params['gps_lat']}',
|
||||
'{$params['gps_lon']}',
|
||||
'{$params['origin_name']}',
|
||||
'{$params['file_name']}',
|
||||
'Y',
|
||||
'{$params['cam_date']}',
|
||||
NOW(),
|
||||
'{$params['upload_path']}',
|
||||
'Y',
|
||||
'{$params['upload_path']}',
|
||||
'{$params['thumb_name']}',
|
||||
'Y'
|
||||
)
|
||||
";
|
||||
|
||||
if ($this->db->query($sql) === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => '저장실패',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
|
||||
// 동영상 정보 저장
|
||||
public function saveVideo($params)
|
||||
{
|
||||
$sql = "INSERT INTO apt_photo
|
||||
(rcpt_no, pho_cate1, pho_cate2, filenm, filenm_up, insert_tm, file_path, use_yn, cloud_upload_yn)
|
||||
VALUES
|
||||
(
|
||||
{$params['rcpt_no']},
|
||||
'V',
|
||||
'V01',
|
||||
'{$params['origin_name']}',
|
||||
'{$params['file_name']}',
|
||||
NOW(),
|
||||
'{$params['upload_path']}',
|
||||
'Y',
|
||||
'Y'
|
||||
)
|
||||
";
|
||||
|
||||
// print ($sql);
|
||||
|
||||
if ($this->db->query($sql) === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => '저장실패',
|
||||
];
|
||||
}
|
||||
|
||||
$sql = "UPDATE apt_result" .
|
||||
" SET vdo_up_ynx = 'Y'" .
|
||||
" ,not_vdo_reson = ''" .
|
||||
" ,video_target = 'Y'" .
|
||||
" ,not_vdo_tm = NULL " .
|
||||
" ,vdo_up_tm = NOW() " .
|
||||
" ,apt_step = 'S03'" .
|
||||
" WHERE rcpt_no = {$params['rcpt_no']}";
|
||||
|
||||
$this->db->query($sql);
|
||||
|
||||
// print ($sql);
|
||||
|
||||
//히스토리
|
||||
$this->saveHistory($params['rcpt_no'], 'S03', 'F', 'F1', session('usr_id'));
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// 업로드파일 일괄삭제
|
||||
public function removeAllPho($rcpt_no)
|
||||
{
|
||||
$sql = "UPDATE apt_photo" .
|
||||
" SET use_yn = 'N'" .
|
||||
" WHERE rcpt_no = ? ";
|
||||
|
||||
if ($this->db->query($sql, [$rcpt_no]) === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => '저장실패',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
|
||||
// 선택파일 삭제
|
||||
public function removePho($params)
|
||||
{
|
||||
$builder = $this->db->table('apt_photo');
|
||||
|
||||
$builder->whereIn('pho_no', $params);
|
||||
|
||||
$result = $builder->update([
|
||||
'use_yn' => 'N',
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => 'DB 업데이트 실패',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
|
||||
// 카테고리 지정
|
||||
public function updatePhoCate($data)
|
||||
{
|
||||
$sql = "UPDATE apt_photo SET
|
||||
pho_cate1 = '{$data['code1']}',
|
||||
pho_cate2 = '{$data['code2']}'
|
||||
WHERE pho_no = {$data['pho_no']}
|
||||
";
|
||||
|
||||
if ($this->db->query($sql) === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'msg' => '저장 실패',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class CodeModel extends Model
|
||||
->getResultArray();
|
||||
}
|
||||
|
||||
public function getCategoryCodeList($category = array(), $useYn = '')
|
||||
public function getCategoryCodeList($category = [], $useYn = '')
|
||||
{
|
||||
$this->db->select('category, cd, cd_nm, use_yn');
|
||||
$this->db->from('codes');
|
||||
|
||||
@@ -42,6 +42,31 @@
|
||||
background-color: #ff0000 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.dropzone {
|
||||
min-height: 260px;
|
||||
background-color: #fafbfc;
|
||||
}
|
||||
|
||||
.dropzone:hover {
|
||||
background-color: #f4f6f8;
|
||||
}
|
||||
|
||||
#myDropzone {
|
||||
position: relative;
|
||||
padding-top: 110px;
|
||||
/* 메시지 영역 높이만큼 */
|
||||
}
|
||||
|
||||
#uploadModal .modal-dialog {
|
||||
max-width: 1140px;
|
||||
/* modal-xl */
|
||||
}
|
||||
|
||||
#uploadModal .modal-content {
|
||||
height: 450px;
|
||||
max-height: 450px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>아파트단지 DB구축 상세</h1>
|
||||
@@ -85,12 +110,11 @@
|
||||
<td>
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-md-9">
|
||||
<!-- ✅ 원본: value="" <?= $apt['memo'] ?> -> HTML 속성 깨짐 -->
|
||||
<input class="form-control" type="text" id="memo"
|
||||
value="<?= esc($apt['memo'] ?? '') ?>" />
|
||||
</div>
|
||||
<div class="col-md-3 text-end">
|
||||
<button type="button" class="btn btn-outline-light"
|
||||
<button type="button" class="btn btn-outline-focus"
|
||||
onclick="saveMemo('<?= esc($apt['rcpt_no'] ?? '') ?>')">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,7 +179,7 @@
|
||||
</td>
|
||||
|
||||
<td style="text-align:center">
|
||||
<button type="button" class="btn btn-sm btn-outline-light"
|
||||
<button type="button" class="btn btn-sm btn-outline-focus"
|
||||
onclick="saveKeeper('<?= esc($apt['rcpt_no'] ?? '') ?>')">저장</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -227,7 +251,7 @@
|
||||
<h5 class="mb-2">단지 특이사항</h5>
|
||||
|
||||
<textarea name="note" id="note" class="form-control mb-2"
|
||||
style="height: 220px;"><?= esc($apt['note'] ?? '') ?></textarea>
|
||||
style="height: 220px;resize: none;"><?= esc($apt['note'] ?? '') ?></textarea>
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="ms-auto">
|
||||
@@ -279,10 +303,21 @@
|
||||
<tr>
|
||||
<th rowspan="2" style="text-align: center;">동영상</th>
|
||||
<td rowspan="2" style="text-align: center;">
|
||||
<img src="/plugin/img/photo.gif" alt="비디오" style="padding: 3px;"><br>
|
||||
<button class="btn btn-sm btn-outline-success" type="button" id="btn_upload_video">
|
||||
<i class="fa fa-fw" aria-hidden="true" title="Copy to use file-image-o"></i> 파일
|
||||
</button>
|
||||
<div class="d-flex flex-column align-items-center gap-2">
|
||||
<?php if (!empty($apt['vdo_path'])): ?>
|
||||
<button class="btn btn-sm btn-danger" type="button" id="btn_preview_video"
|
||||
data-video-src="<?= esc($apt['vdo_path']) ?>"
|
||||
onclick="fn_preview('<?= esc($apt['vdo_path']) ?>', 'vdo')">
|
||||
<i class="fa fa-play-circle me-1"></i> 동영상확인
|
||||
</button>
|
||||
|
||||
<?php else: ?>
|
||||
<img src="/plugin/img/photo.gif" alt="비디오" style="padding: 3px;"><br>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-sm btn-outline-success" type="button" id="btn_upload_video">
|
||||
<i class="fa fa-fw" aria-hidden="true" title="Copy to use file-image-o"></i> 파일
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<th style="text-align: center;">동영상 촬영불가</th>
|
||||
<td style="text-align: center;">
|
||||
@@ -352,17 +387,181 @@
|
||||
</td>
|
||||
<th style="text-align:center">사진 일괄삭제</th>
|
||||
<td style="text-align:center">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger">삭제</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||
onclick="fn_pho_remove(<?= $apt['rcpt_no'] ?>, 'all');">삭제</button>
|
||||
</td>
|
||||
<th style="text-align:center">단지정보 작성완료</th>
|
||||
<td style="text-align:center">
|
||||
<button type="button" class="btn btn-sm btn-outline-light">저장</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-focus"
|
||||
onclick="fn_write_complete(<?= $apt['rcpt_no'] ?>);">저장</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// 카테고리 없는 이미지만 추출
|
||||
$arrPho = [];
|
||||
foreach ($image as $key => $val) {
|
||||
if (empty($val['pho_cate2'])) {
|
||||
$arrPho[] = $val;
|
||||
unset($image[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($arrPho)):
|
||||
?>
|
||||
<style>
|
||||
/* 썸네일 카드 */
|
||||
.pho-grid {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.pho-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.pho-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.pho-item {
|
||||
border: 1px solid #e6e9ef;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
|
||||
}
|
||||
|
||||
.pho-thumb {
|
||||
position: relative;
|
||||
aspect-ratio: 4 / 3;
|
||||
background: #f6f7f9;
|
||||
}
|
||||
|
||||
.pho-thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pho-check {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
background: rgba(255, 255, 255, .92);
|
||||
border: 1px solid rgba(0, 0, 0, .06);
|
||||
padding: 6px 8px;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.pho-meta {
|
||||
padding: 10px 12px;
|
||||
border-top: 1px solid #f0f2f6;
|
||||
}
|
||||
|
||||
.pho-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.pho-sub {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
margin: 4px 0 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
<div class="main-card mb-3 card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
<h5 class="card-title mb-0">카테고리 미지정 이미지</h5>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="form-check mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="chkAllPho">
|
||||
<label class="form-check-label" for="chkAllPho">전체선택</label>
|
||||
</div>
|
||||
<span class="badge bg-secondary"><?= count($arrPho) ?>건</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pho-grid mt-3">
|
||||
<?php foreach ($arrPho as $row):
|
||||
$link = (string) ($row['file_path'] ?? '') . (string) ($row['filenm_up'] ?? '');
|
||||
$thumb = (string) ($row['thumb_path'] ?? '') . (string) ($row['thumb_nm'] ?? '');
|
||||
$fileNm = $row['filenm'] ?? '';
|
||||
|
||||
if (($row['cloud_upload_yn'] ?? '') === 'Y') {
|
||||
$link = NCLOUD_OBJECT_STORAGE_URL . $link;
|
||||
$thumb = NCLOUD_OBJECT_STORAGE_URL . $thumb;
|
||||
}
|
||||
|
||||
$phoNo = $row['pho_no'] ?? '';
|
||||
|
||||
?>
|
||||
<div class="pho-item">
|
||||
<div class="pho-thumb">
|
||||
<a onclick="fn_preview('<?= esc($link) ?>')" rel="lightbox">
|
||||
<img src="<?= esc($thumb) ?>" alt="<?= esc($fileNm) ?>" loading="lazy">
|
||||
</a>
|
||||
|
||||
<div class="pho-check">
|
||||
<input class="form-check-input m-0" type="checkbox" name="phoNo[]"
|
||||
value="<?= esc($phoNo) ?>" id="phoNo_<?= esc($phoNo) ?>">
|
||||
<label class="small mb-0" for="phoNo_<?= esc($phoNo) ?>">선택</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end align-items-center gap-3">
|
||||
<div class="d-flex justify-content-end align-items-center gap-1 flex-wrap">
|
||||
<select class="form-select form-select-sm w-auto" id="pho_cate1">
|
||||
<option value="">- 대분류 -</option>
|
||||
<?php if (!empty($code1)): ?>
|
||||
<?php foreach ($code1 as $c): ?>
|
||||
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
|
||||
<select class="form-select form-select-sm w-auto" id="pho_cate2">
|
||||
<option value="">- 소분류 -</option>
|
||||
</select>
|
||||
|
||||
<button class="btn btn-sm btn-primary" onclick="fn_pho_save(<?= $apt['rcpt_no'] ?>);">
|
||||
저장
|
||||
</button>
|
||||
|
||||
<button class="btn btn-sm btn-danger" onclick="fn_pho_remove(<?= $apt['rcpt_no'] ?>, 'select');">
|
||||
삭제
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="main-card mb-3 card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">단지 사진 및 설명 정보</h5>
|
||||
@@ -493,7 +692,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border: 0;">
|
||||
<a href="<?= esc($link) ?>" rel="lightbox">
|
||||
<a onclick="fn_preview('<?= esc($link) ?>')" rel="lightbox">
|
||||
<img src="<?= esc($thumb) ?>" style="<?= esc($borderStyle) ?>" />
|
||||
</a>
|
||||
</td>
|
||||
@@ -635,7 +834,7 @@
|
||||
<!-- ✅ textarea 안에 PHP 로직 제거 -->
|
||||
<textarea class="form-control" name="pho_explain"
|
||||
id="pho_explain_<?= esc($c2cd) ?>"
|
||||
style="width:350px;height:90px;"><?= esc($phoExplain) ?></textarea>
|
||||
style="width:350px;height:90px;resize: none;"><?= esc($phoExplain) ?></textarea>
|
||||
</td>
|
||||
<td style="border: 0; padding: 0;">
|
||||
<!-- <span id="count_<?= esc($c2cd) ?>" style="font-weight:bold">0</span> -->
|
||||
@@ -671,7 +870,7 @@
|
||||
</div>
|
||||
<?php
|
||||
if (in_array(session('usr_level'), array('1', '2', '70'))): ?>
|
||||
<div class="card-footer card-footer d-flex justify-content-center">
|
||||
<div class="card-footer d-flex justify-content-center">
|
||||
<button class="mb-2 me-2 btn btn-success"
|
||||
onclick="ajax_saveCheck('<?= $apt['rcpt_no'] ?>','<?= $apt['hscp_no'] ?>');">검수</button>
|
||||
<button class="mb-2 me-2 btn btn-warning"
|
||||
@@ -734,24 +933,31 @@
|
||||
<input type="hidden" name="rcpt_no" value="<?= $apt['rcpt_no'] ?>">
|
||||
<input type="hidden" name="upload_type" value="photo">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<!-- 전체 업로드 -->
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-block btn-outline-success" type="button" id="btnUpload">
|
||||
<i class="pe-7s-up-arrow"></i> 전체업로드 </button>
|
||||
</div>
|
||||
<!-- 버튼 툴바 -->
|
||||
<div class="d-flex justify-content-end gap-2 mb-3" style="padding: 16px 10px 0 0;">
|
||||
<button type="button" class="btn btn-primary" id="uploadPick">
|
||||
<i class="pe-7s-up-arrow"></i> 파일선택
|
||||
</button>
|
||||
|
||||
<!-- 업로드 취소 -->
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-block btn-outline-danger" type="button" id="btnRemove">
|
||||
<i class="pe-7s-less"></i> 업로드취소 </button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" id="btnUpload">
|
||||
<i class="pe-7s-up-arrow"></i> 파일업로드
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<button type="button" class="btn btn-danger" id="btnRemove">
|
||||
<i class="pe-7s-less"></i> 업로드취소
|
||||
</button>
|
||||
</div>
|
||||
<div id="myDropzone" class="form-group dropzone">
|
||||
|
||||
<!-- Dropzone 영역 -->
|
||||
<div id="myDropzone" class="dropzone border rounded-3 p-4"
|
||||
style="max-height: 400px;overflow-y: scroll;">
|
||||
<div class="dz-message dz-message-fixed needsclick text-center">
|
||||
<i class="pe-7s-upload mb-2" style="font-size:42px;"></i><br>
|
||||
<strong class="fs-6">파일을 드래그하거나 클릭해서 추가하세요</strong><br>
|
||||
<small class="text-muted">
|
||||
사진 여러 장 가능 / 동영상은 1개만
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -773,6 +979,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="previewModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">미리보기</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body p-0">
|
||||
<img id="imgPreview" src="" alt="미리보기" width="100%" height="500px">
|
||||
<video id="vdoPreview" controls playsinline preload="metadata"
|
||||
style="display: none;height: 500px;width: 100%;">
|
||||
<source id="videoSource" src="" type="video/mp4">
|
||||
브라우저가 video 태그를 지원하지 않습니다.
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<style>
|
||||
@@ -788,6 +1013,8 @@
|
||||
const teamArr = <?= json_encode($team ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
const userArr = <?= json_encode($user ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
const code2Arr = <?= json_encode($code2 ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
var map, marker;
|
||||
|
||||
Dropzone.autoDiscover = false;
|
||||
@@ -836,8 +1063,8 @@
|
||||
map = new naver.maps.Map('mapArea', {
|
||||
center: new naver.maps.LatLng(lat, lng),
|
||||
useStyleMap: true,
|
||||
zoom: 15,
|
||||
minZoom: 1,
|
||||
zoom: 17,
|
||||
minZoom: 10,
|
||||
mapTypeControl: true,
|
||||
mapTypeControlOptions: {
|
||||
style: naver.maps.MapTypeControlStyle.BUTTON,
|
||||
@@ -898,7 +1125,7 @@
|
||||
uploadMultiple: true,
|
||||
parallelUploads: 20,
|
||||
maxFilesize: 100,
|
||||
acceptedFiles: '.jpeg,.jpg,.JPEG,.JPG,.mpg,.MP4',
|
||||
acceptedFiles: '.jpeg,.jpg,.JPEG,.JPG,.webp,.mpg,.MP4',
|
||||
addRemoveLinks: true,
|
||||
dictRemoveFile: "삭제",
|
||||
dictDefaultMessage: "파일을 여기에 드래그하거나 클릭해서 추가하세요",
|
||||
@@ -949,6 +1176,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let isFormDataAppended = false;
|
||||
dz.on("sending", function (file, xhr, formData) {
|
||||
if (isFormDataAppended) return;
|
||||
@@ -959,12 +1187,30 @@
|
||||
isFormDataAppended = true;
|
||||
});
|
||||
|
||||
dz.on("completeMultiple", function () {
|
||||
dz.on("queuecomplete", function () {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
dz.on("successmultiple", function () {
|
||||
isFormDataAppended = false;
|
||||
});
|
||||
dz.on("errormultiple", function () {
|
||||
isFormDataAppended = false;
|
||||
});
|
||||
dz.on("canceledmultiple", function () {
|
||||
isFormDataAppended = false;
|
||||
});
|
||||
dz.on("processingmultiple", function () { });
|
||||
|
||||
// 업로드파일 선택
|
||||
$("#uploadPick").on("click", function () {
|
||||
isFormDataAppended = false;
|
||||
dz.hiddenFileInput.click();
|
||||
});
|
||||
|
||||
$("#btnUpload").on("click", function () {
|
||||
dz.processQueue(); // 업로드 실행
|
||||
isFormDataAppended = false;
|
||||
});
|
||||
|
||||
$("#btnRemove").on("click", function () {
|
||||
@@ -979,6 +1225,49 @@
|
||||
dz.removeFile(file);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 체크박스 전체선택
|
||||
$("#chkAllPho").on("click", function () {
|
||||
const isChecked = $(this).is(":checked");
|
||||
|
||||
$("input[name='phoNo[]']").prop("checked", isChecked);
|
||||
|
||||
});
|
||||
|
||||
$("input[name='phoNo[]']").on("change", function () {
|
||||
const total = $("input[name='phoNo[]']").length;
|
||||
const checked = $("input[name='phoNo[]']:checked").length;
|
||||
|
||||
$("#chkAllPho").prop("checked", total === checked);
|
||||
});
|
||||
|
||||
// 카테고리 onchange
|
||||
$("#pho_cate1").on("change", function (e) {
|
||||
|
||||
const val = e.target.value;
|
||||
|
||||
var str = "";
|
||||
str = "<option value=''>-소분류-</option>";
|
||||
$.getJSON("/article/apt/cateJson?pho_cate1=" + val, function (result) {
|
||||
var total = result.length;
|
||||
for (var i = 0; i < total; i++) {
|
||||
var cateNm = result[i].cd_nm;
|
||||
|
||||
if (total == 1) {
|
||||
str += "<option value=\"" + result[i].cd + "\" selected>" + cateNm + "</option>";
|
||||
} else {
|
||||
str += "<option value=\"" + result[i].cd + "\">" + cateNm + "</option>";
|
||||
}
|
||||
}
|
||||
|
||||
$("#pho_cate2").html(str);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1443,6 +1732,148 @@
|
||||
}
|
||||
|
||||
|
||||
// 사진 카테고리 저장
|
||||
function fn_pho_save(rcpt_no) {
|
||||
const code1 = $("#pho_cate1").val();
|
||||
const code2 = $("#pho_cate2").val();
|
||||
|
||||
if (code1 === "") {
|
||||
Swal.fire({
|
||||
title: "대분류를 선택해주세요.",
|
||||
icon: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (code2 === "") {
|
||||
Swal.fire({
|
||||
title: "소분류를 선택해주세요.",
|
||||
icon: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var phoNos = $('input[name="phoNo[]"]:checked')
|
||||
.map(function () { return this.value; })
|
||||
.get();
|
||||
|
||||
|
||||
if (phoNos.length === 0) {
|
||||
Swal.fire({
|
||||
title: "사진을 선택해주세요.",
|
||||
icon: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
data = {
|
||||
'rcpt_no': rcpt_no,
|
||||
'phoNo': phoNos,
|
||||
'code1': code1,
|
||||
'code2': code2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
swal.fire({
|
||||
text: "저장 하시겠습니까?",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "예",
|
||||
cancelButtonText: "아니오",
|
||||
closeOnConfirm: false,
|
||||
closeOnCancel: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
callAjax("/article/apt/savePhoCate", data, fn_result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 사진 일괄삭제
|
||||
function fn_pho_remove(rcpt_no, type) {
|
||||
var data = new Array();
|
||||
if (type === "all") {
|
||||
data = {
|
||||
'type': type,
|
||||
'rcpt_no': rcpt_no
|
||||
};
|
||||
} else if (type === "select") {
|
||||
var phoNos = $('input[name="phoNo[]"]:checked')
|
||||
.map(function () { return this.value; })
|
||||
.get();
|
||||
|
||||
|
||||
if (phoNos.length === 0) {
|
||||
Swal.fire({
|
||||
title: "삭제할 사진을 선택해주세요.",
|
||||
icon: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
data = {
|
||||
'type': type,
|
||||
phoNo: phoNos,
|
||||
};
|
||||
}
|
||||
|
||||
if (data === null) {
|
||||
Swal.fire({
|
||||
title: "데이터 누락",
|
||||
icon: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var title = "삭제 하시겠습니까?"
|
||||
|
||||
swal.fire({
|
||||
text: title,
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "예",
|
||||
cancelButtonText: "아니오",
|
||||
closeOnConfirm: false,
|
||||
closeOnCancel: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
callAjax("/article/apt/reqRemovePho", data, fn_result);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 단지정보 작성완료
|
||||
function fn_write_complete(rcpt_no) {
|
||||
|
||||
swal.fire({
|
||||
text: "저장하시겠습니까?",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "예",
|
||||
cancelButtonText: "아니오",
|
||||
closeOnConfirm: false,
|
||||
closeOnCancel: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
var data = {
|
||||
'rcpt_no': rcpt_no
|
||||
};
|
||||
callAjax("/article/apt/saveWriteComplete", data, fn_result);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 검수 저장
|
||||
function ajax_saveCheck(rcpt_no, hscp_no) {
|
||||
var stat = '<?php echo $apt['apt_step'] ?>';
|
||||
@@ -1567,6 +1998,11 @@
|
||||
icon: "success",
|
||||
draggable: true
|
||||
})
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: result.msg,
|
||||
@@ -1609,6 +2045,42 @@
|
||||
|
||||
$("#mapModal").modal("show");
|
||||
}
|
||||
|
||||
// 이미지 프리뷰
|
||||
function fn_preview(src, type = 'img') {
|
||||
const $img = $('#imgPreview');
|
||||
const $video = $('#vdoPreview');
|
||||
const video = document.getElementById('vdoPreview');
|
||||
const source = document.getElementById('videoSource');
|
||||
|
||||
if (type === 'vdo') {
|
||||
// 이미지 숨김
|
||||
$img.hide().attr('src', '');
|
||||
|
||||
// video source 세팅
|
||||
source.src = '<?= NCLOUD_OBJECT_STORAGE_URL ?>' + src;
|
||||
|
||||
// video 표시 + 로드
|
||||
$video.show();
|
||||
video.load();
|
||||
|
||||
$('#previewTitle').text('동영상 미리보기');
|
||||
} else {
|
||||
// video 정지 및 초기화
|
||||
video.pause();
|
||||
source.src = '';
|
||||
video.load();
|
||||
$video.hide();
|
||||
|
||||
// 이미지 표시
|
||||
$img.attr('src', src).show();
|
||||
|
||||
$('#previewTitle').text('이미지 미리보기');
|
||||
}
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('previewModal'));
|
||||
modal.show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
Reference in New Issue
Block a user