지정판매소 주소·지도 연동과 관련 설정을 반영
지정판매소 등록/수정/목록에 카카오 주소 검색 및 지도 연동 컴포넌트를 적용하고, 관련 모델·SQL 스크립트·테스트 설정을 함께 정리해 기능 동작 기반을 맞췄다. Made-with: Cursor
This commit is contained in:
126
app/Views/components/kakao_address_search.php
Normal file
126
app/Views/components/kakao_address_search.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/** @var string $buttonId 주소 검색 버튼 id */
|
||||
$buttonId = $buttonId ?? 'btn-kakao-postcode';
|
||||
/** @var string $zipName 우편번호 input name */
|
||||
$zipName = $zipName ?? 'ds_zip';
|
||||
/** @var string $roadName 도로명 input name */
|
||||
$roadName = $roadName ?? 'ds_addr';
|
||||
/** @var string $jibunName 지번 input name */
|
||||
$jibunName = $jibunName ?? 'ds_addr_jibun';
|
||||
/** @var string $sidoFieldName 카카오 시·도 → hidden name (비우면 미설정) */
|
||||
$sidoFieldName = $sidoFieldName ?? '';
|
||||
/** @var string $sigunguFieldName 카카오 시·군·구 → hidden name */
|
||||
$sigunguFieldName = $sigunguFieldName ?? '';
|
||||
/** @var string $detailFieldName 상세주소 input name (건물명 등, 비우면 미사용) */
|
||||
$detailFieldName = $detailFieldName ?? '';
|
||||
/**
|
||||
* @var array{lg_sido?: string, lg_gugun?: string}|null $tenantScope 지자체 관할 검사(비우면 미검사)
|
||||
*/
|
||||
$tenantScope = $tenantScope ?? null;
|
||||
/** @var bool $roadBaseOnly true면 도로명에 건물명 괄호 미부착(상세로 이전) */
|
||||
$roadBaseOnly = ! empty($roadBaseOnly);
|
||||
?>
|
||||
<script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
var btnId = <?= json_encode($buttonId, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var zipName = <?= json_encode($zipName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var roadName = <?= json_encode($roadName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var jibunName = <?= json_encode($jibunName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var sidoFieldName = <?= json_encode($sidoFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var sigunguFieldName = <?= json_encode($sigunguFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var detailFieldName = <?= json_encode($detailFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var roadBaseOnly = <?= $roadBaseOnly ? 'true' : 'false' ?>;
|
||||
var tenantScope = <?= json_encode($tenantScope ?? (object) [], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
|
||||
function compactStr(s) {
|
||||
return String(s || '').replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
function tokenMatches(needle, primary, blob) {
|
||||
var n = compactStr(needle);
|
||||
if (!n) return true;
|
||||
var b = compactStr(blob);
|
||||
if (b.indexOf(n) !== -1) return true;
|
||||
var p = compactStr(primary);
|
||||
if (p.indexOf(n) !== -1) return true;
|
||||
if (n.indexOf(p) !== -1 && p) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function addressAllowedByTenant(data) {
|
||||
var lgSido = tenantScope && tenantScope.lg_sido ? String(tenantScope.lg_sido) : '';
|
||||
var lgGugun = tenantScope && tenantScope.lg_gugun ? String(tenantScope.lg_gugun) : '';
|
||||
if (!lgSido && !lgGugun) return true;
|
||||
var sido = data.sido || '';
|
||||
var sigungu = data.sigungu || '';
|
||||
var road = data.roadAddress || '';
|
||||
var jibun = data.jibunAddress || '';
|
||||
var zip = data.zonecode || '';
|
||||
var blob = sido + ' ' + sigungu + ' ' + road + ' ' + jibun + ' ' + zip;
|
||||
if (lgSido && !tokenMatches(lgSido, sido, blob)) return false;
|
||||
if (lgGugun && !tokenMatches(lgGugun, sigungu, blob)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function bind() {
|
||||
var btn = document.getElementById(btnId);
|
||||
if (!btn) return;
|
||||
var form = btn.closest('form');
|
||||
if (!form) return;
|
||||
|
||||
function field(n) {
|
||||
return form.querySelector('[name="' + n + '"]');
|
||||
}
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
if (typeof daum === 'undefined' || !daum.Postcode) {
|
||||
window.alert('주소 검색 스크립트를 불러오지 못했습니다. 네트워크를 확인해 주세요.');
|
||||
return;
|
||||
}
|
||||
new daum.Postcode({
|
||||
oncomplete: function (data) {
|
||||
if (!addressAllowedByTenant(data)) {
|
||||
window.alert('작업 중인 지자체 관할이 아닌 주소입니다. 해당 시·구 주소를 검색해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
var zipEl = field(zipName);
|
||||
var roadEl = field(roadName);
|
||||
var jibunEl = field(jibunName);
|
||||
if (zipEl) zipEl.value = data.zonecode || '';
|
||||
|
||||
var roadAddr = data.roadAddress || '';
|
||||
if (!roadBaseOnly && data.buildingName !== '') {
|
||||
roadAddr += (roadAddr !== '' ? ' (' + data.buildingName + ')' : data.buildingName);
|
||||
}
|
||||
if (roadEl) roadEl.value = roadAddr;
|
||||
|
||||
if (jibunEl) jibunEl.value = data.jibunAddress || '';
|
||||
|
||||
if (sidoFieldName) {
|
||||
var sidoEl = field(sidoFieldName);
|
||||
if (sidoEl) sidoEl.value = data.sido || '';
|
||||
}
|
||||
if (sigunguFieldName) {
|
||||
var sigEl = field(sigunguFieldName);
|
||||
if (sigEl) sigEl.value = data.sigungu || '';
|
||||
}
|
||||
if (detailFieldName && roadBaseOnly) {
|
||||
var detEl = field(detailFieldName);
|
||||
if (detEl) detEl.value = data.buildingName || '';
|
||||
}
|
||||
}
|
||||
}).open();
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', bind);
|
||||
} else {
|
||||
bind();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user