From b9dd24082c857e8ce204f117c60e4cc49c190c34 Mon Sep 17 00:00:00 2001 From: taekyoungc Date: Mon, 15 Jun 2026 13:31:31 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=99=94=EB=A9=B4=EC=84=A4=EB=AA=85=20?= =?UTF-8?q?=EC=86=8C=EC=A0=9C=EB=AA=A9=20=EC=8A=A4=ED=81=AC=EB=A1=A4=C2=B7?= =?UTF-8?q?=EA=B0=95=EC=A1=B0=20+=20=EA=B8=80=EC=94=A8=ED=81=AC=EA=B8=B0?= =?UTF-8?q?=20=EB=A9=94=EB=89=B4=20=ED=99=95=EB=8C=80=20+=20=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EC=96=B4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - screenHelp 앵커(?hl=)로 '이 화면 설명' 클릭 시 해당 소제목으로 스크롤·강조, 재오픈 시 재강조(postMessage) - 글씨 크기(A−/A+)가 상단 대메뉴·좌측 사이드바까지 확대, 관리자 페이지에도 조절 기능 추가 - 화면 설명 드로어 양방향 리사이즈(좁히기 가능) + 기본 너비 2배 Co-Authored-By: Claude Opus 4.8 --- app/Config/Manual.php | 49 ++++++++++++++++-------------- app/Helpers/admin_helper.php | 22 ++++++++++---- app/Views/admin/layout.php | 24 +++++++++++++++ app/Views/bag/layout/embed.php | 18 ++++++++--- app/Views/bag/layout/portal.php | 24 ++++++++++----- app/Views/bag/layout/workspace.php | 10 ++++-- app/Views/bag/manual.php | 32 +++++++++++++++++++ 7 files changed, 137 insertions(+), 42 deletions(-) diff --git a/app/Config/Manual.php b/app/Config/Manual.php index f17e3a6..251bb91 100644 --- a/app/Config/Manual.php +++ b/app/Config/Manual.php @@ -40,34 +40,37 @@ class Manual extends BaseConfig * "이 화면 설명" 버튼이 현재 경로로 알맞은 매뉴얼 페이지를 연다. * 더 긴(구체적) 접두가 우선하도록 길이 내림차순으로 매칭한다. * + * 값에 "slug#소제목힌트" 형식으로 적으면, 매뉴얼 창이 열릴 때 해당 소제목으로 + * 자동 스크롤하고 잠시 강조 표시한다(힌트는 그 페이지의 H2/H3 텍스트 일부와 일치). + * * @var array */ public array $screenHelp = [ - 'bag/order/phone' => 'sales', - 'bag/order' => 'order', - 'bag/bag-orders' => 'order', - 'bag/receiving' => 'order', - 'bag/bag-receivings' => 'order', - 'bag/inventory' => 'inventory', - 'bag/sale' => 'sales', - 'bag/sales' => 'sales', - 'bag/issue' => 'sales', - 'bag/bag-issues' => 'sales', - 'bag/bag-sales' => 'sales', - 'bag/shop-orders' => 'sales', + 'bag/order/phone' => 'sales#전화 주문 접수', + 'bag/order' => 'order#발주 등록', + 'bag/bag-orders' => 'order#발주 현황', + 'bag/receiving' => 'order#입고 처리', + 'bag/bag-receivings' => 'order#입고 현황', + 'bag/inventory' => 'inventory#재고 현황', + 'bag/sale' => 'sales#지정판매소 판매', + 'bag/sales' => 'sales#지정판매소 판매', + 'bag/issue' => 'sales#무료용 불출 처리', + 'bag/bag-issues' => 'sales#무료용 불출 처리', + 'bag/bag-sales' => 'sales#판매/반품 현황', + 'bag/shop-orders' => 'sales#전화 주문 접수', 'bag/flow' => 'reports', - 'bag/reports' => 'reports', + 'bag/reports' => 'reports#일계표', 'bag/analytics' => 'reports', - 'bag/designated-shops' => 'basic', - 'bag/bag-prices' => 'basic', - 'bag/prices' => 'basic', - 'bag/packaging-units' => 'basic', - 'bag/code-kinds' => 'basic', - 'bag/code-details' => 'basic', - 'bag/managers' => 'basic', - 'bag/companies' => 'basic', - 'bag/sales-agencies' => 'basic', - 'bag/free-recipients' => 'basic', + 'bag/designated-shops' => 'basic#지정판매소 관리', + 'bag/bag-prices' => 'basic#단가 관리', + 'bag/prices' => 'basic#단가 관리', + 'bag/packaging-units' => 'basic#포장 단위 관리', + 'bag/code-kinds' => 'basic#기본코드 관리', + 'bag/code-details' => 'basic#기본코드 관리', + 'bag/managers' => 'basic#그 밖의 기본정보', + 'bag/companies' => 'basic#그 밖의 기본정보', + 'bag/sales-agencies' => 'basic#그 밖의 기본정보', + 'bag/free-recipients' => 'basic#그 밖의 기본정보', 'bag/number-lookup' => 'codes', ]; } diff --git a/app/Helpers/admin_helper.php b/app/Helpers/admin_helper.php index aba61be..c01f69a 100644 --- a/app/Helpers/admin_helper.php +++ b/app/Helpers/admin_helper.php @@ -950,18 +950,28 @@ if (! function_exists('manual_help_url_for_path')) { return ''; } $map = config(\Config\Manual::class)->screenHelp ?? []; - $bestSlug = ''; - $bestLen = -1; - foreach ($map as $prefix => $slug) { + $bestVal = ''; + $bestLen = -1; + foreach ($map as $prefix => $val) { $p = strtolower((string) $prefix); if ($path === $p || str_starts_with($path . '/', $p . '/')) { if (strlen($p) > $bestLen) { - $bestLen = strlen($p); - $bestSlug = (string) $slug; + $bestLen = strlen($p); + $bestVal = (string) $val; } } } + if ($bestVal === '') { + return ''; + } - return $bestSlug !== '' ? base_url('bag/manual/' . $bestSlug) : ''; + // 값은 "slug" 또는 "slug#소제목힌트" 형식. 힌트가 있으면 ?hl= 로 전달해 해당 소제목으로 스크롤·강조. + [$slug, $hint] = array_pad(explode('#', $bestVal, 2), 2, null); + $url = base_url('bag/manual/' . $slug); + if ($hint !== null && trim($hint) !== '') { + $url .= '?hl=' . rawurlencode(trim($hint)); + } + + return $url; } } diff --git a/app/Views/admin/layout.php b/app/Views/admin/layout.php index 54894cc..9ee77b5 100644 --- a/app/Views/admin/layout.php +++ b/app/Views/admin/layout.php @@ -105,6 +105,11 @@ tailwind.config = { base_url('/')]) ?>
+
+ + 100% + +
· · 님 @@ -161,6 +166,25 @@ tailwind.config = { window.addEventListener('pageshow', function (e) { if (e.persisted) closeStuckOverlays(); }); window.addEventListener('pagehide', closeStuckOverlays); })(); + + // 글씨 크기 조절(A−/A+) — 본문 + 상단 대메뉴 + 좌측 사이드바에 zoom 적용. 사이트/워크스페이스와 배율 공유. + (function () { + var FONT_KEY = 'jrj_font_scale'; + var scaleSelectors = ['.portal-header', '.sidebar', '.work-main']; + function curScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; } + function applyScale(s) { + s = Math.min(150, Math.max(70, s)); + try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {} + var z = s / 100; + scaleSelectors.forEach(function (sel) { var el = document.querySelector(sel); if (el) el.style.zoom = z; }); + var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%'; + } + applyScale(curScale()); + var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus'); + if (plus) plus.addEventListener('click', function () { applyScale(curScale() + 10); }); + if (minus) minus.addEventListener('click', function () { applyScale(curScale() - 10); }); + window.addEventListener('storage', function (e) { if (e.key === FONT_KEY) applyScale(curScale()); }); + })(); diff --git a/app/Views/bag/layout/embed.php b/app/Views/bag/layout/embed.php index b479673..7a9ab30 100644 --- a/app/Views/bag/layout/embed.php +++ b/app/Views/bag/layout/embed.php @@ -51,7 +51,7 @@ tailwind.config = { .data-table tbody tr:hover td { background-color: #f9fafb; } @media print { .no-print { display: none !important; } .embed-titlebar { display: none; } } /* 화면 설명 드로어(팝업) — 현재 화면 위 오른쪽에 겹쳐 띄움 */ - .help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; } + .help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(920px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; } .help-drawer.open { display: flex; } .help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; } .help-drawer-head .hd-btns { display: flex; gap: 4px; } @@ -137,7 +137,13 @@ tailwind.config = { function withEmbedUrl(url) { try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); return x.href; } catch (e) { return url; } } function openHelp(url) { var u = withEmbedUrl(url); - if (dFrame.getAttribute('data-src') !== u) { dFrame.src = u; dFrame.setAttribute('data-src', u); } + if (dFrame.getAttribute('data-src') !== u) { + // 새 URL → 재로드(로드 후 매뉴얼 자체 스크립트가 강조 실행) + dFrame.src = u; dFrame.setAttribute('data-src', u); + } else { + // 같은 URL → 재로드 안 함. 매뉴얼에 다시 강조하라고 알림(껐다 켜도 강조되도록) + try { dFrame.contentWindow.postMessage({ type: 'manual-hl' }, location.origin); } catch (e) {} + } var tab = document.getElementById('helpDrawerTab'); if (tab) tab.setAttribute('href', url); drawer.classList.add('open'); } @@ -160,9 +166,13 @@ tailwind.config = { // 드로어 폭 드래그 조절 (function () { var grip = document.getElementById('helpDrawerGrip'), dragging = false; - grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; document.body.style.userSelect = 'none'; }); + // 드래그 중 iframe 이 마우스 이벤트를 가로채면(특히 좁힐 때) 멈추므로, 화면 전체 투명 오버레이로 이벤트를 가로챈다. + var ov = document.createElement('div'); + ov.style.cssText = 'position:fixed;inset:0;z-index:10000;cursor:col-resize;display:none;'; + document.body.appendChild(ov); + grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; ov.style.display = 'block'; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; }); - document.addEventListener('mouseup', function () { dragging = false; document.body.style.userSelect = ''; }); + document.addEventListener('mouseup', function () { if (!dragging) return; dragging = false; ov.style.display = 'none'; document.body.style.userSelect = ''; }); })(); // 글씨 크기(zoom) — 상단바에서 조절한 값을 적용. localStorage 공유 + storage 이벤트로 실시간 반영. diff --git a/app/Views/bag/layout/portal.php b/app/Views/bag/layout/portal.php index 8698b8e..e49a5b4 100644 --- a/app/Views/bag/layout/portal.php +++ b/app/Views/bag/layout/portal.php @@ -217,7 +217,7 @@ tailwind.config = {