Compare commits
5 Commits
287691328e
...
71534fb198
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71534fb198 | ||
|
|
e6a49e379a | ||
|
|
77c7d3e119 | ||
|
|
b9dd24082c | ||
|
|
56dadb3478 |
@@ -27,6 +27,7 @@ class Filters extends BaseFilters
|
||||
public array $aliases = [
|
||||
'adminAuth' => \App\Filters\AdminAuthFilter::class,
|
||||
'loginAuth' => \App\Filters\LoginAuthFilter::class,
|
||||
'embedRedirect' => \App\Filters\EmbedRedirectFilter::class,
|
||||
'csrf' => CSRF::class,
|
||||
'toolbar' => DebugToolbar::class,
|
||||
'honeypot' => Honeypot::class,
|
||||
@@ -81,6 +82,7 @@ class Filters extends BaseFilters
|
||||
'after' => [
|
||||
// 'honeypot',
|
||||
// 'secureheaders',
|
||||
'embedRedirect', // 임베드(탭) 리다이렉트에 embed=1 유지 → 중첩 셸 방지
|
||||
],
|
||||
];
|
||||
|
||||
@@ -108,5 +110,10 @@ class Filters extends BaseFilters
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
public array $filters = [];
|
||||
public array $filters = [
|
||||
// 모든 업무(bag) 화면은 로그인 필요. 세션 만료 후 어떤 버튼을 눌러도
|
||||
// 깨진 화면 대신 로그인으로 리다이렉트되도록 일괄 보호한다.
|
||||
// (login/logout/register 는 bag/* 가 아니므로 영향 없음. 관리자 화면은 adminAuth 가 별도 처리)
|
||||
'loginAuth' => ['before' => ['bag', 'bag/*']],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -40,34 +40,37 @@ class Manual extends BaseConfig
|
||||
* "이 화면 설명" 버튼이 현재 경로로 알맞은 매뉴얼 페이지를 연다.
|
||||
* 더 긴(구체적) 접두가 우선하도록 길이 내림차순으로 매칭한다.
|
||||
*
|
||||
* 값에 "slug#소제목힌트" 형식으로 적으면, 매뉴얼 창이 열릴 때 해당 소제목으로
|
||||
* 자동 스크롤하고 잠시 강조 표시한다(힌트는 그 페이지의 H2/H3 텍스트 일부와 일치).
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
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',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -592,6 +592,7 @@ class Bag extends BaseController
|
||||
'selectedKind' => $selectedKind,
|
||||
'detailList' => $detailList,
|
||||
'rowCanEdit' => $rowCanEdit,
|
||||
'isEmbed' => $this->isEmbeddedRequest(), // 행 클릭 시 embed 유지(중첩 셸 방지)
|
||||
], true); // 본문이 이미 카드 2개라 바깥 래퍼 생략
|
||||
}
|
||||
|
||||
@@ -4768,14 +4769,18 @@ SQL);
|
||||
$receiverIdx = $this->parseReceiverRefToStoredIdx($lgIdx, $receiverRef);
|
||||
$senderIdx = (int) ($this->request->getPost('br_sender_idx') ?? 0);
|
||||
|
||||
// 오류 시 명시적으로 일괄 입고 화면으로 복귀(back()은 도움말 드로어 등이 남긴
|
||||
// previous_url 로 잘못 이동할 수 있어 사용하지 않는다)
|
||||
$batchUrl = site_url('bag/receiving/batch?company_idx=' . $companyIdx . '&bag_code=' . rawurlencode($bagCode));
|
||||
|
||||
if (empty($selected)) {
|
||||
return redirect()->back()->withInput()->with('error', '일괄 입고할 행을 선택해 주세요.');
|
||||
return redirect()->to($batchUrl)->withInput()->with('error', '일괄 입고할 행을 선택해 주세요.');
|
||||
}
|
||||
if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $receiveDate)) {
|
||||
return redirect()->back()->withInput()->with('error', '입고일 형식을 확인해 주세요.');
|
||||
return redirect()->to($batchUrl)->withInput()->with('error', '입고일 형식을 확인해 주세요.');
|
||||
}
|
||||
if ($receiverIdx <= 0) {
|
||||
return redirect()->back()->withInput()->with('error', '인수자를 선택해 주세요.');
|
||||
return redirect()->to($batchUrl)->withInput()->with('error', '인수자를 선택해 주세요.');
|
||||
}
|
||||
|
||||
$senderResolved = $this->resolveCompanySenderName($lgIdx, $senderIdx);
|
||||
@@ -4816,7 +4821,7 @@ SQL);
|
||||
}
|
||||
|
||||
if (empty($insertRows)) {
|
||||
return redirect()->back()->withInput()->with('error', '선택한 행에 입고할 수량이 없습니다.');
|
||||
return redirect()->to($batchUrl)->withInput()->with('error', '선택한 행에 입고할 수량이 없습니다.');
|
||||
}
|
||||
|
||||
$recvModel = model(BagReceivingModel::class);
|
||||
@@ -4846,7 +4851,7 @@ SQL);
|
||||
$db->transComplete();
|
||||
|
||||
if (! $db->transStatus()) {
|
||||
return redirect()->back()->withInput()->with('error', '일괄 입고 처리 중 오류가 발생했습니다.');
|
||||
return redirect()->to($batchUrl)->withInput()->with('error', '일괄 입고 처리 중 오류가 발생했습니다.');
|
||||
}
|
||||
|
||||
return redirect()->to(site_url('bag/receiving/batch?company_idx=' . $companyIdx . '&bag_code=' . rawurlencode($bagCode)))
|
||||
|
||||
@@ -25,6 +25,12 @@ class Home extends BaseController
|
||||
return view('bag/layout/workspace');
|
||||
}
|
||||
|
||||
// 워크스페이스 탭(iframe) 안에서 세션이 만료된 경우: 공개 랜딩 대신 로그인으로 보내
|
||||
// 로그인 페이지의 프레임 이탈 스크립트가 상위 창 전체를 로그인으로 전환하게 한다.
|
||||
if ($this->isEmbeddedRequest()) {
|
||||
return redirect()->to(base_url('login'));
|
||||
}
|
||||
|
||||
return view('welcome_message');
|
||||
}
|
||||
|
||||
|
||||
54
app/Filters/EmbedRedirectFilter.php
Normal file
54
app/Filters/EmbedRedirectFilter.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 워크스페이스 탭(iframe, embed=1) 안에서 발생한 리다이렉트가 embed 파라미터를 잃지 않도록
|
||||
* 응답 Location 헤더에 embed=1 을 유지시킨다.
|
||||
*
|
||||
* 배경: 폼 전송 후 redirect()->to(...) 하면 Location 에 embed 가 빠지고, iframe 안에서
|
||||
* 그 주소로 다시 GET 하면(특히 HTTP 환경에서 Sec-Fetch-Dest 미전송) 전체 셸(헤더·사이드바)이
|
||||
* 한 번 더 렌더되어 "화면 안에 전체 화면이 또" 생긴다. 이를 전역에서 한 번에 방지한다.
|
||||
*/
|
||||
class EmbedRedirectFilter implements FilterInterface
|
||||
{
|
||||
public function before(RequestInterface $request, $arguments = null)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string>|null $arguments
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
|
||||
{
|
||||
// 임베드 요청에서 시작된 리다이렉트만 처리
|
||||
if ($request->getGet('embed') === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$location = $response->getHeaderLine('Location');
|
||||
if ($location === '') {
|
||||
return; // 리다이렉트 응답이 아님
|
||||
}
|
||||
|
||||
// 이미 embed 파라미터가 있으면 그대로 둔다
|
||||
if (preg_match('/[?&]embed=/', $location) === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 인증 흐름(로그인/로그아웃/회원가입)은 상위 프레임에서 처리하므로 embed 를 붙이지 않는다
|
||||
if (preg_match('#/(login|logout|register)(/|\?|$)#', $location) === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sep = strpos($location, '?') !== false ? '&' : '?';
|
||||
$response->setHeader('Location', $location . $sep . 'embed=1');
|
||||
}
|
||||
}
|
||||
@@ -950,18 +950,28 @@ if (! function_exists('manual_help_url_for_path')) {
|
||||
return '';
|
||||
}
|
||||
$map = config(\Config\Manual::class)->screenHelp ?? [];
|
||||
$bestSlug = '';
|
||||
$bestVal = '';
|
||||
$bestLen = -1;
|
||||
foreach ($map as $prefix => $slug) {
|
||||
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;
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,26 @@
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
</div>
|
||||
</section>
|
||||
<div class="border border-gray-300 rounded-lg p-4 mt-2">
|
||||
<form method="get" action="<?= base_url('admin/access/approvals') ?>" class="mb-4 flex flex-wrap items-center gap-2 text-sm">
|
||||
<style>
|
||||
/* 인쇄 시 승인 대기 표를 A4 너비에 맞춤(잘림 방지) */
|
||||
@media print {
|
||||
@page { size: A4 portrait; margin: 10mm; }
|
||||
.approval-print-wrap { border: 0 !important; padding: 0 !important; overflow: visible !important; }
|
||||
.approval-print-wrap table.data-table { width: 100% !important; table-layout: fixed; font-size: 9px; }
|
||||
.approval-print-wrap .data-table th,
|
||||
.approval-print-wrap .data-table td {
|
||||
white-space: normal !important; /* nowrap 해제 → 줄바꿈 허용 */
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
padding: 2px 3px !important;
|
||||
vertical-align: top;
|
||||
}
|
||||
/* '관리'(승인/반려 버튼) 열은 인쇄에 불필요 → 제외해 가로 폭 확보 */
|
||||
.approval-print-wrap .col-actions { display: none !important; }
|
||||
}
|
||||
</style>
|
||||
<div class="approval-print-wrap border border-gray-300 rounded-lg p-4 mt-2">
|
||||
<form method="get" action="<?= base_url('admin/access/approvals') ?>" class="no-print mb-4 flex flex-wrap items-center gap-2 text-sm">
|
||||
<label for="status" class="font-bold text-gray-700 shrink-0">상태</label>
|
||||
<select id="status" name="status" class="border border-gray-300 rounded px-3 py-1.5 text-sm min-w-[12rem] w-48 max-w-full">
|
||||
<option value="pending" <?= ($status ?? 'pending') === 'pending' ? 'selected' : '' ?>>승인 대기</option>
|
||||
@@ -25,7 +43,7 @@
|
||||
<th class="text-center">요청 역할</th>
|
||||
<th class="text-center">상태</th>
|
||||
<th class="text-center">처리일</th>
|
||||
<th class="text-center">관리</th>
|
||||
<th class="text-center col-actions">관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -45,7 +63,7 @@
|
||||
<?php if (($row->mar_status ?? '') === 'rejected'): ?>반려<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= esc($row->mar_processed_at ?? '-') ?></td>
|
||||
<td class="text-center">
|
||||
<td class="text-center col-actions">
|
||||
<?php if (($row->mar_status ?? '') === 'pending'): ?>
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<form action="<?= base_url('admin/access/approve/' . $row->mar_idx) ?>" method="post" class="inline">
|
||||
|
||||
@@ -105,6 +105,11 @@ tailwind.config = {
|
||||
<?= view('home/_dashboard_gov_portal_brand', ['brandHref' => base_url('/')]) ?>
|
||||
<?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?>
|
||||
<div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;">
|
||||
<div class="ws-fontctl" title="글씨 크기 조절" style="display:inline-flex;align-items:center;gap:2px;background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);border-radius:6px;padding:1px;">
|
||||
<button type="button" id="wsFontMinus" title="글씨 작게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:11px;line-height:1;border-radius:5px;">A−</button>
|
||||
<span id="wsFontPct" style="min-width:34px;text-align:center;color:#fff;font-size:.68rem;font-weight:600;">100%</span>
|
||||
<button type="button" id="wsFontPlus" title="글씨 크게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:14px;line-height:1;border-radius:5px;">A+</button>
|
||||
</div>
|
||||
<span class="user-line">
|
||||
<?php if ($effectiveLgName !== ''): ?><strong><?= esc($effectiveLgName) ?></strong> · <?php endif; ?>
|
||||
<?= esc($levelName) ?> · <?= esc($mbName) ?>님
|
||||
@@ -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()); });
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -8,7 +8,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="border border-gray-300 rounded-lg p-4 overflow-auto mt-2">
|
||||
<style>
|
||||
/* 인쇄 시 회원 목록 표를 A4 너비에 맞춤(잘림 방지) */
|
||||
@media print {
|
||||
@page { size: A4 portrait; margin: 10mm; }
|
||||
.user-print-wrap { border: 0 !important; padding: 0 !important; overflow: visible !important; }
|
||||
.user-print-wrap table.data-table { width: 100% !important; table-layout: fixed; font-size: 9px; }
|
||||
.user-print-wrap .data-table th,
|
||||
.user-print-wrap .data-table td {
|
||||
white-space: normal !important; /* nowrap 해제 → 줄바꿈 허용 */
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
padding: 2px 3px !important;
|
||||
vertical-align: top;
|
||||
}
|
||||
/* '관리'(작업 버튼) 열은 인쇄에 불필요 → 제외해 가로 폭 확보 */
|
||||
.user-print-wrap .col-actions { display: none !important; }
|
||||
}
|
||||
</style>
|
||||
<div class="user-print-wrap border border-gray-300 rounded-lg p-4 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -20,7 +38,7 @@
|
||||
<th class="text-center">상태</th>
|
||||
<th class="text-left">로그인 잠금</th>
|
||||
<th class="text-left">가입일</th>
|
||||
<th class="text-center">관리</th>
|
||||
<th class="text-center col-actions">관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
@@ -56,7 +74,7 @@
|
||||
?>
|
||||
</td>
|
||||
<td class="text-left pl-2"><?= esc($row->mb_regdate ?? '') ?></td>
|
||||
<td class="text-center">
|
||||
<td class="text-center col-actions">
|
||||
<?php if ((int) $row->mb_state !== 0): ?>
|
||||
<?php if ($loginLocked): ?>
|
||||
<form action="<?= base_url('admin/users/unlock-login/' . $row->mb_idx) ?>" method="POST" class="inline mr-1" onsubmit="return confirm('로그인 잠금을 해제할까요?');">
|
||||
|
||||
@@ -12,6 +12,7 @@ $showKindActions = $canManageKinds;
|
||||
$selectedKindId = (int) ($selectedKind->ck_idx ?? 0);
|
||||
$colCount = 6 + ($showKindActions ? 1 : 0);
|
||||
$detailColCount = 7 + ($canManageDetails ? 1 : 0);
|
||||
$embedQs = ! empty($isEmbed) ? '&embed=1' : ''; // 워크스페이스 탭 안에서는 embed 유지(중첩 셸 방지)
|
||||
|
||||
/** 상태 배지 (업무현황 스타일의 가벼운 pill) */
|
||||
$stateBadge = static function (int $state): string {
|
||||
@@ -51,7 +52,7 @@ $stateBadge = static function (int $state): string {
|
||||
<?php $i = 0; foreach ($codeKinds as $row): $i++; ?>
|
||||
<?php
|
||||
$isSelected = (int) $row->ck_idx === $selectedKindId;
|
||||
$detailUrl = base_url('bag/code-kinds?ck_idx=' . (int) $row->ck_idx);
|
||||
$detailUrl = base_url('bag/code-kinds?ck_idx=' . (int) $row->ck_idx . $embedQs);
|
||||
?>
|
||||
<tr class="border-b border-gray-200 last:border-0 cursor-pointer hover:bg-blue-50/60 <?= $isSelected ? 'bg-blue-50' : '' ?>"
|
||||
onclick="window.location.href='<?= esc($detailUrl, 'attr') ?>'">
|
||||
|
||||
@@ -144,7 +144,7 @@ if ($initialRows === []) {
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-2 mt-2">
|
||||
<section class="border border-gray-300 rounded-lg bg-white">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 가능 봉투(현재 재고)</div>
|
||||
<div class="overflow-auto max-h-[300px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -180,7 +180,7 @@ if ($initialRows === []) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg bg-white">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">최근 불출 내역</div>
|
||||
<div class="overflow-auto max-h-[300px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<span class="text-gray-600 ml-2">왼쪽 목록에서 삭제할 발주를 선택한 뒤 「삭제 실행」을 누르세요.</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
|
||||
<section class="xl:col-span-5 border border-gray-300 rounded-lg bg-white">
|
||||
<section class="xl:col-span-5 border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
|
||||
<div class="overflow-auto max-h-[410px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -189,7 +189,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
|
||||
<section class="xl:col-span-5 border border-gray-300 rounded-lg bg-white">
|
||||
<section class="xl:col-span-5 border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
|
||||
<div class="overflow-auto max-h-[410px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -222,7 +222,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="xl:col-span-7 border border-gray-300 rounded-lg bg-white">
|
||||
<section class="xl:col-span-7 border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700"><?= $editMode ? '발주 변경 수정' : '발주 Form' ?></div>
|
||||
<div class="p-2 space-y-2">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
@@ -331,7 +331,7 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg bg-white">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 등록 종류</div>
|
||||
<div class="overflow-auto">
|
||||
<table class="w-full data-table text-sm order-reference-table">
|
||||
|
||||
@@ -26,7 +26,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
|
||||
</form>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 리스트</div>
|
||||
<div class="max-h-64 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -52,7 +52,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg">
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 취소 리스트</div>
|
||||
<div class="max-h-64 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -85,7 +85,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 취소 봉투 코드</div>
|
||||
<div class="max-h-72 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -22,7 +22,7 @@ $returns = is_array($returns ?? null) ? $returns : [];
|
||||
</form>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 리스트</div>
|
||||
<div class="max-h-64 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -50,7 +50,7 @@ $returns = is_array($returns ?? null) ? $returns : [];
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg">
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 리스트</div>
|
||||
<div class="max-h-64 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -79,7 +79,7 @@ $returns = is_array($returns ?? null) ? $returns : [];
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 취소 대상 봉투 코드</div>
|
||||
<div class="max-h-72 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<div id="scan-message" class="text-sm text-gray-600"></div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 정보</div>
|
||||
<table class="w-full text-sm">
|
||||
<tr><th class="text-left px-3 py-1.5 w-28">판매소 코드</th><td id="shop-info-code" class="px-3 py-1.5">-</td></tr>
|
||||
@@ -46,7 +46,7 @@
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg">
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">주문 접수 리스트</div>
|
||||
<div class="max-h-56 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -97,7 +97,7 @@
|
||||
</section>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-3">
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 내역</div>
|
||||
<div class="max-h-72 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -118,7 +118,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 상세 내역</div>
|
||||
<div class="max-h-72 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -28,7 +28,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
|
||||
<div id="scan-message" class="text-sm text-gray-600"></div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-3">
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 정보</div>
|
||||
<table class="w-full text-sm">
|
||||
<tr><th class="text-left px-3 py-1.5 w-28">판매소 코드</th><td id="shop-info-code" class="px-3 py-1.5">-</td></tr>
|
||||
@@ -39,7 +39,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 리스트</div>
|
||||
<div class="max-h-64 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -70,7 +70,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 봉투 코드</div>
|
||||
<div class="max-h-72 overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -42,7 +42,6 @@ foreach ($subtotals as $subtotal) {
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||
<a href="<?= base_url('bag/inventory/export?' . http_build_query(['base_date' => $baseDate, 'agency_idx' => $agencyIdx])) ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
|
||||
<button type="button" onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
<a href="<?= base_url('bag/inventory/inspection-select') ?>" class="no-print border border-blue-300 text-blue-700 px-3 py-1 rounded-sm text-sm hover:bg-blue-50 transition">실사 선별 조회</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -74,7 +74,7 @@ $sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
|
||||
</section>
|
||||
|
||||
<section class="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
||||
<div class="border border-gray-300 rounded-lg bg-white">
|
||||
<div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div>
|
||||
<div class="overflow-auto max-h-[500px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -134,7 +134,7 @@ $sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="border border-gray-300 rounded-lg bg-white">
|
||||
<div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(선택 박스 조회)</div>
|
||||
<form method="post" action="<?= base_url('bag/inventory/inspection-select/save') ?>" id="inspection-save-form">
|
||||
<?= csrf_field() ?>
|
||||
@@ -224,7 +224,7 @@ $sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="border border-gray-300 rounded-lg bg-white">
|
||||
<div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(선택 낱장 코드 조회)</div>
|
||||
<div class="overflow-auto max-h-[240px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -61,7 +61,7 @@ foreach ($sheetRows as $row) {
|
||||
</section>
|
||||
|
||||
<section class="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
||||
<div class="border border-gray-300 rounded-lg bg-white">
|
||||
<div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div>
|
||||
<div class="overflow-auto max-h-[500px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -114,7 +114,7 @@ foreach ($sheetRows as $row) {
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="border border-gray-300 rounded-lg bg-white">
|
||||
<div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(읽기 전용)</div>
|
||||
<div class="overflow-auto max-h-[260px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -169,7 +169,7 @@ foreach ($sheetRows as $row) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border border-gray-300 rounded-lg bg-white">
|
||||
<div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(읽기 전용)</div>
|
||||
<div class="overflow-auto max-h-[240px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -73,7 +73,7 @@ $selectedBagCode = (string) ($selectedBagCode ?? '');
|
||||
<input type="hidden" name="sel_bag_code" value="<?= esc($selectedBagCode) ?>"/>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-4 gap-2">
|
||||
<section class="border border-gray-300 rounded-lg bg-white xl:col-span-1">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden bg-white xl:col-span-1">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 리스트</div>
|
||||
<div class="overflow-auto max-h-[560px]">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -120,7 +120,7 @@ $selectedBagCode = (string) ($selectedBagCode ?? '');
|
||||
</section>
|
||||
|
||||
<div class="xl:col-span-3 grid grid-cols-1 gap-2">
|
||||
<section class="border border-gray-300 rounded-lg bg-white">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 품목 내역</div>
|
||||
<div class="overflow-auto max-h-[270px]">
|
||||
<table class="w-full data-table text-sm" id="detail-table">
|
||||
@@ -196,7 +196,7 @@ $selectedBagCode = (string) ($selectedBagCode ?? '');
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg bg-white">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 품목 코드</div>
|
||||
<div class="overflow-auto max-h-[250px]">
|
||||
<table class="w-full data-table text-sm" id="code-table">
|
||||
|
||||
@@ -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 이벤트로 실시간 반영.
|
||||
|
||||
@@ -217,7 +217,7 @@ tailwind.config = {
|
||||
|
||||
<!-- 화면 설명 드로어(팝업) -->
|
||||
<style>
|
||||
.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-btn { color: #fff; background: rgba(255,255,255,.14); border: 0; width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; margin-left: 4px; }
|
||||
@@ -238,7 +238,12 @@ tailwind.config = {
|
||||
var drawer = document.getElementById('helpDrawer'), dFrame = document.getElementById('helpDrawerFrame');
|
||||
function openHelp(url) {
|
||||
var u = url; try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); u = x.href; } catch (e) {}
|
||||
if (dFrame.getAttribute('data-src') !== u) { dFrame.src = u; dFrame.setAttribute('data-src', u); }
|
||||
if (dFrame.getAttribute('data-src') !== u) {
|
||||
dFrame.src = u; dFrame.setAttribute('data-src', u);
|
||||
} else {
|
||||
// 같은 URL → 재로드 안 함. 매뉴얼에 다시 강조하라고 알림(껐다 켜도 강조되도록)
|
||||
try { dFrame.contentWindow.postMessage({ type: 'manual-hl' }, location.origin); } catch (e) {}
|
||||
}
|
||||
drawer.classList.add('open');
|
||||
}
|
||||
function closeHelp() { drawer.classList.remove('open'); }
|
||||
@@ -251,18 +256,23 @@ tailwind.config = {
|
||||
document.getElementById('helpDrawerClose').addEventListener('click', closeHelp);
|
||||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeHelp(); });
|
||||
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 = ''; });
|
||||
|
||||
// 글씨 크기 조절 — 본문(.work-main) 영역에 zoom 적용. 헤더/사이드바는 그대로.
|
||||
// 글씨 크기 조절 — 본문 + 상단 대메뉴 + 좌측 사이드바(메뉴)에 zoom 적용.
|
||||
var FONT_KEY = 'jrj_font_scale';
|
||||
var target = document.querySelector('.work-main') || document.body;
|
||||
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) {}
|
||||
target.style.zoom = (s / 100);
|
||||
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());
|
||||
|
||||
@@ -486,11 +486,17 @@ if ($effectiveLgIdx) {
|
||||
function setFontScale(s) {
|
||||
s = Math.min(150, Math.max(70, s));
|
||||
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
|
||||
var z = s / 100;
|
||||
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
|
||||
Object.keys(tabs).forEach(function (k) { try { tabs[k].frame.contentDocument.documentElement.style.zoom = (s / 100); } catch (e) {} });
|
||||
// 탭(iframe) 내용
|
||||
Object.keys(tabs).forEach(function (k) { try { tabs[k].frame.contentDocument.documentElement.style.zoom = z; } catch (e) {} });
|
||||
// 셸 메뉴(상단 대메뉴 + 좌측 사이드바)도 함께 확대
|
||||
['.portal-header', '.sidebar'].forEach(function (sel) {
|
||||
var el = document.querySelector(sel); if (el) el.style.zoom = z;
|
||||
});
|
||||
}
|
||||
(function () {
|
||||
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = curFontScale() + '%';
|
||||
setFontScale(curFontScale()); // 저장된 배율을 셸 메뉴에도 적용(초기 로드)
|
||||
var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus');
|
||||
if (plus) plus.addEventListener('click', function () { setFontScale(curFontScale() + 10); });
|
||||
if (minus) minus.addEventListener('click', function () { setFontScale(curFontScale() - 10); });
|
||||
|
||||
@@ -44,6 +44,9 @@ $searchQ = (string) (service('request')->getGet('q') ?? '');
|
||||
.manual-prose tbody tr:nth-child(even) td { background: #f9fafb; }
|
||||
.manual-prose hr { margin: 1.6rem 0; border: 0; border-top: 1px solid #e5e7eb; }
|
||||
.manual-toc a.active { background: #1a2b4b; color: #fff; font-weight: 700; }
|
||||
/* "이 화면 설명"으로 들어왔을 때 해당 소제목 강조 */
|
||||
.manual-prose h2.hl-flash, .manual-prose h3.hl-flash { background: #fef9c3; box-shadow: -6px 0 0 #fef9c3, 6px 0 0 #fef9c3; border-radius: 4px; animation: hlFlash 2.6s ease-out 1; }
|
||||
@keyframes hlFlash { 0%, 45% { background: #fde047; box-shadow: -6px 0 0 #fde047, 6px 0 0 #fde047; } 100% { background: transparent; box-shadow: -6px 0 0 transparent, 6px 0 0 transparent; } }
|
||||
@media print {
|
||||
.manual-toc, .manual-actions, .manual-nav { display: none !important; }
|
||||
.manual-layout { display: block !important; }
|
||||
@@ -108,6 +111,35 @@ $searchQ = (string) (service('request')->getGet('q') ?? '');
|
||||
var SEARCH_URL = location.origin + <?= json_encode((string) parse_url(base_url('bag/manual/search'), PHP_URL_PATH), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var BASE = location.origin + <?= json_encode((string) parse_url(base_url('bag/manual/'), PHP_URL_PATH), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||
var Q0 = <?= json_encode($searchQ, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS) ?>;
|
||||
// "이 화면 설명"으로 들어온 경우: 해당 소제목으로 스크롤 + 강조.
|
||||
// 드로어를 껐다 다시 켜도(같은 URL이라 iframe 재로드 안 됨) 부모가 보낸 메시지로 다시 강조한다.
|
||||
var hlFlashTimer = null;
|
||||
function runHl() {
|
||||
var hl = '';
|
||||
try { hl = new URLSearchParams(location.search).get('hl') || ''; } catch (e) {}
|
||||
hl = hl.trim();
|
||||
if (!hl) return;
|
||||
var norm = function (s) { return String(s || '').replace(/\s+/g, ' ').trim().toLowerCase(); };
|
||||
var needle = norm(hl);
|
||||
var prose = document.querySelector('.manual-prose');
|
||||
if (!prose) return;
|
||||
var heads = prose.querySelectorAll('h2, h3'), target = null;
|
||||
for (var i = 0; i < heads.length; i++) {
|
||||
if (norm(heads[i].textContent).indexOf(needle) >= 0) { target = heads[i]; break; }
|
||||
}
|
||||
if (!target) return;
|
||||
clearTimeout(hlFlashTimer);
|
||||
target.classList.remove('hl-flash');
|
||||
void target.offsetWidth; // 리플로우 → 애니메이션 재시작
|
||||
target.classList.add('hl-flash');
|
||||
try { target.scrollIntoView({ block: 'start', behavior: 'smooth' }); } catch (e) { target.scrollIntoView(); }
|
||||
hlFlashTimer = setTimeout(function () { target.classList.remove('hl-flash'); }, 2700);
|
||||
}
|
||||
runHl();
|
||||
window.addEventListener('message', function (e) {
|
||||
if (e.origin !== location.origin) return;
|
||||
if (e.data && e.data.type === 'manual-hl') runHl();
|
||||
});
|
||||
var input = document.getElementById('manualSearchInput');
|
||||
var box = document.getElementById('manualSearchResults');
|
||||
|
||||
|
||||
@@ -18,12 +18,7 @@
|
||||
</section>
|
||||
|
||||
<div class="mt-2 space-y-3">
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php /* 플래시 메시지는 레이아웃(embed/portal)에서 한 번만 표시 — 중복 출력 방지 */ ?>
|
||||
|
||||
<form method="get" action="<?= base_url('bag/order/change') ?>" class="border border-gray-300 rounded-lg bg-white p-3 flex flex-wrap items-end gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -55,7 +50,7 @@
|
||||
</form>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-2">
|
||||
<section class="lg:col-span-5 border border-gray-300 rounded-lg bg-white">
|
||||
<section class="lg:col-span-5 border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 목록 (발주일 · 제작업체)</div>
|
||||
<div class="overflow-auto max-h-[min(420px,60vh)]">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -65,7 +65,7 @@ for ($year = $baseYear - 2; $year <= $baseYear + 2; $year++) {
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="border border-gray-300 rounded-lg mt-2 bg-white">
|
||||
<section class="border border-gray-300 rounded-lg overflow-hidden mt-2 bg-white">
|
||||
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주목록</div>
|
||||
<div class="overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
|
||||
@@ -3,16 +3,7 @@
|
||||
</section>
|
||||
|
||||
<div class="border border-gray-300 rounded-lg p-4 mt-2 bg-white">
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mb-3 border border-emerald-300 bg-emerald-50 text-emerald-800 px-3 py-2 rounded-sm text-sm">
|
||||
<?= esc(session()->getFlashdata('success')) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="mb-3 border border-red-300 bg-red-50 text-red-700 px-3 py-2 rounded-sm text-sm">
|
||||
<?= esc(session()->getFlashdata('error')) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php /* success·error 플래시는 레이아웃(embed/portal)에서 한 번만 표시 — 중복 방지. (검증 errors 목록만 여기서 표시) */ ?>
|
||||
<?php $flashErrors = session()->getFlashdata('errors'); ?>
|
||||
<?php if (is_array($flashErrors) && $flashErrors !== []): ?>
|
||||
<div class="mb-3 border border-red-300 bg-red-50 text-red-700 px-3 py-2 rounded-sm text-sm">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</section>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3 mt-2">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg bg-white">
|
||||
<section class="xl:col-span-2 border border-gray-300 rounded-lg bg-white overflow-hidden">
|
||||
<div class="px-3 py-2 border-b border-gray-200 bg-gray-50 text-sm font-semibold text-gray-700">접수 리스트(전화)</div>
|
||||
<div class="max-h-[72vh] overflow-auto">
|
||||
<table class="w-full data-table text-sm">
|
||||
@@ -38,7 +38,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg bg-white">
|
||||
<section class="xl:col-span-3 border border-gray-300 rounded-lg bg-white overflow-hidden">
|
||||
<div class="px-3 py-2 border-b border-gray-200 bg-gray-50 text-sm font-semibold text-gray-700">상세 정보</div>
|
||||
|
||||
<form id="order-detail-form" action="<?= base_url('bag/order/phone/manage/update') ?>" method="POST" class="p-3 space-y-3">
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mt-2 border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="mt-2 border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php /* 플래시 메시지는 레이아웃(embed/portal)에서 한 번만 표시한다 — 중복 출력 방지 */ ?>
|
||||
|
||||
<section class="p-2 bg-white border border-gray-300 rounded-lg mt-2">
|
||||
<form method="get" action="<?= base_url('bag/receiving/batch') ?>" class="flex flex-wrap items-center gap-2">
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mt-2 border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="mt-2 border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php /* 플래시 메시지는 레이아웃(embed/portal)에서 한 번만 표시 — 중복 출력 방지 */ ?>
|
||||
|
||||
<section class="p-2 bg-white border border-gray-300 rounded-lg mt-2">
|
||||
<form method="get" action="<?= base_url('bag/receiving/scanner') ?>" class="flex flex-wrap items-end gap-2">
|
||||
|
||||
Reference in New Issue
Block a user