Files
jongryangje/app/Views/bag/flow.php
taekyoungc a8afaf4af2 style: 표/패널 UI 전면 통일 + 화면설명 드로어·글씨크기·탭 개선
표 디자인
- 모든 표를 가벼운 스타일로 통일(.data-table 경량화: 작은 회색 헤더·연한 구분선·hover)
- 표/패널 바깥 테두리 둥글게(rounded-lg) 일괄 적용, 표 래퍼에 패딩 카드(p-4) 통일
- 표 헤더·데이터 정렬을 전 화면 좌측 기준으로 통일
  - .data-table th/td text-align:left (전역), 흩어진 center/right 정렬 정리
  - 재디자인 Tailwind 표(포장단위·단가·기본코드·담당자·업체·판매대행소·무료대상자·지정판매소)도 셀 좌측화
- 기본정보관리 등 나머지 소메뉴 표를 기본 코드 관리 스타일(가벼운 표·상태 pill)로 재디자인

워크스페이스/공통
- "이 화면 설명" → 새 탭 대신 우측 드로어 팝업(현재 화면과 동시에 보기, Esc·드래그 폭조절)
- 상단바 글씨 크기 조절(A−/A+), 작업 내용에 zoom 적용
- 탭 최대치 도달 시 자동 삭제 대신 안내 토스트, "모두 닫기"(업무 현황 탭은 보존)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 17:26:36 +09:00

334 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$startDate = (string) ($startDate ?? date('Y-m-01'));
$endDate = (string) ($endDate ?? date('Y-m-d'));
$aggMode = (string) ($aggMode ?? 'period');
$bagCode = (string) ($bagCode ?? '');
$bagKind = (string) ($bagKind ?? '');
$saIdx = (int) ($saIdx ?? 0);
$rows = is_array($rows ?? null) ? $rows : [];
$bagProducts = is_array($bagProducts ?? null) ? $bagProducts : [];
$bagKindOptions = is_array($bagKindOptions ?? null) ? $bagKindOptions : [];
$agencies = is_array($agencies ?? null) ? $agencies : [];
$queried = (bool) ($queried ?? false);
$exportParams = array_filter([
'search' => '1',
'start_date' => $startDate,
'end_date' => $endDate,
'agg_mode' => $aggMode,
'bag_code' => $bagCode,
'bag_kind' => $bagKind,
'sa_idx' => $saIdx > 0 ? (string) $saIdx : '',
], static fn ($v) => $v !== null && $v !== '');
$excelUrl = $queried
? base_url('bag/flow/export') . '?' . http_build_query($exportParams)
: '';
$fmt = static fn ($n): string => number_format((int) $n);
$printExtraLines = [];
if ($queried) {
$aggLabel = $aggMode === 'daily' ? '일자별' : '기간별';
$printExtraLines[] = '조회기간: ' . $startDate . ' ~ ' . $endDate . ' (' . $aggLabel . ')';
}
$tipPage = "조회 기간 동안 봉투 품목별 입고·출고·잔량을 집계하는 수불표입니다.\n"
. "· 집계방식: 일자별(날짜마다) / 기간별(기간 합계)\n"
. "· 전일재고: 조회 시작일 전날 기준 재고(입고·반품·기타 출고 누적)\n"
. "· 입고: 입고·반품·기타 / 출고: 판매·일반·무료불출·반품·기타\n"
. "· 대행소 선택 시 판매 열만 해당 대행소 소속 판매소 기준\n"
. "조회 후 표·엑셀·인쇄에 반영됩니다.";
?>
<div class="flow-print-sheet">
<?= view('components/print_header', [
'printTitle' => '기간별 봉투 수불 현황',
'printExtraLines' => $printExtraLines,
]) ?>
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel no-print">
<div class="flex flex-wrap items-center justify-between gap-y-2">
<span class="text-sm font-bold text-gray-700 inline-flex items-center gap-1">
기간별 봉투 수불 현황
<?= view('components/field_tooltip', ['text' => $tipPage, 'placement' => 'below']) ?>
</span>
<div class="flex flex-wrap items-center gap-2">
<?php if ($excelUrl !== ''): ?>
<a href="<?= esc($excelUrl, 'attr') ?>" target="_blank" rel="noopener noreferrer"
class="bg-green-700 text-white px-3 py-1 rounded-sm text-sm hover:bg-green-800">엑셀저장</a>
<?php else: ?>
<span class="bg-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm cursor-not-allowed" title="조회 후 이용">엑셀저장</span>
<?php endif; ?>
<button type="button" onclick="window.print()" class="border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">인쇄</button>
<a href="<?= base_url('dashboard') ?>" class="border border-gray-400 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">종료</a>
</div>
</div>
</section>
<section class="p-2 bg-white border-b border-gray-200 no-print">
<form method="get" action="<?= base_url('bag/flow') ?>" class="flex flex-wrap items-end gap-x-3 gap-y-2 text-sm">
<input type="hidden" name="search" value="1"/>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">조회기간</label>
<input type="date" name="start_date" value="<?= esc($startDate) ?>" class="border border-gray-300 rounded px-2 py-1" required/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate) ?>" class="border border-gray-300 rounded px-2 py-1" required/>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">봉투형식</label>
<select name="bag_code" class="border border-gray-300 rounded px-2 py-1 min-w-[11rem]">
<option value="">전체 봉투</option>
<?php foreach ($bagProducts as $bp): ?>
<option value="<?= esc((string) $bp['code']) ?>" <?= $bagCode === (string) $bp['code'] ? 'selected' : '' ?>>
<?= esc((string) $bp['code']) ?> — <?= esc((string) $bp['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">봉투구분</label>
<select name="bag_kind" class="border border-gray-300 rounded px-2 py-1 min-w-[8rem]">
<option value="">전체</option>
<?php foreach ($bagKindOptions as $opt): ?>
<option value="<?= esc((string) $opt->cd_code) ?>" <?= $bagKind === (string) $opt->cd_code ? 'selected' : '' ?>>
<?= esc((string) $opt->cd_name) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">대행소</label>
<select name="sa_idx" class="border border-gray-300 rounded px-2 py-1 min-w-[10rem]">
<option value="0">전체</option>
<?php foreach ($agencies as $agency): ?>
<?php
$aid = (int) ($agency->sa_idx ?? 0);
$label = (string) ($agency->sa_name ?? '');
if (isset($agency->sa_kind) && (string) $agency->sa_kind !== '') {
$label = (string) $agency->sa_kind . ' — ' . $label;
}
?>
<option value="<?= $aid ?>" <?= $saIdx === $aid ? 'selected' : '' ?>><?= esc($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-wrap items-center gap-3">
<span class="font-bold text-gray-700 whitespace-nowrap">집계방식</span>
<label class="inline-flex items-center gap-1">
<input type="radio" name="agg_mode" value="daily" <?= $aggMode === 'daily' ? 'checked' : '' ?>/>
일자별
</label>
<label class="inline-flex items-center gap-1">
<input type="radio" name="agg_mode" value="period" <?= $aggMode === 'period' ? 'checked' : '' ?>/>
기간별
</label>
</div>
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm">조회</button>
<a href="<?= base_url('bag/flow') ?>" class="text-gray-500 hover:text-gray-800 px-2">초기화</a>
</form>
</section>
<?php if (! $queried): ?>
<div class="m-2 p-3 border border-blue-200 bg-blue-50 text-sm text-blue-900 no-print">
조회 조건을 설정한 뒤 <strong>조회</strong> 버튼을 눌러 주세요.
</div>
<?php endif; ?>
<?php if ($queried): ?>
<div class="p-2 overflow-auto flow-report-wrap">
<table class="w-full data-table text-sm flow-report-table">
<thead>
<tr>
<th rowspan="2" class="flow-col-date text-center">일자</th>
<th rowspan="2" class="flow-col-item text-left">품목</th>
<th rowspan="2" class="flow-col-num text-right">
<span class="flow-lbl-screen">전일재고</span><span class="flow-lbl-print">전일</span>
</th>
<th colspan="4">입고</th>
<th colspan="6">출고</th>
<th rowspan="2" class="flow-col-num text-right">잔량</th>
</tr>
<tr>
<th class="flow-col-num text-right">입고</th>
<th class="flow-col-num text-right">반품</th>
<th class="flow-col-num text-right">기타</th>
<th class="flow-col-num text-right">
<span class="flow-lbl-screen">입고계</span><span class="flow-lbl-print">입계</span>
</th>
<th class="flow-col-num text-right">판매</th>
<th class="flow-col-num text-right">
<span class="flow-lbl-screen">일반불출</span><span class="flow-lbl-print">일반</span>
</th>
<th class="flow-col-num text-right">
<span class="flow-lbl-screen">무료불출</span><span class="flow-lbl-print">무료</span>
</th>
<th class="flow-col-num text-right">반품</th>
<th class="flow-col-num text-right">기타</th>
<th class="flow-col-num text-right">
<span class="flow-lbl-screen">출고계</span><span class="flow-lbl-print">출계</span>
</th>
</tr>
</thead>
<tbody class="text-right">
<?php if ($rows !== []): ?>
<?php foreach ($rows as $row): ?>
<?php
$rowType = (string) ($row['row_type'] ?? 'data');
$trClass = match ($rowType) {
'subtotal', 'grand' => 'bg-amber-50 font-semibold',
default => '',
};
?>
<tr class="<?= esc($trClass) ?>">
<td class="flow-col-date text-center"><?= esc((string) ($row['date'] ?? '')) ?></td>
<td class="flow-col-item text-left pl-2"><?= esc((string) ($row['item_name'] ?? '')) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['prev_stock'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_in'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_return'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_misc'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_total'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_sale'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_issue_gen'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_issue_free'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_return'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_misc'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_total'] ?? 0) ?></td>
<td class="flow-col-num font-semibold tabular-nums"><?= $fmt($row['balance'] ?? 0) ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="15" class="text-center text-gray-400 py-8">조회 결과가 없습니다.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<style>
.field-tip { position: relative; display: inline-flex; vertical-align: middle; }
.field-tip-btn {
display: inline-flex; align-items: center; justify-content: center;
width: 14px; height: 14px; font-size: 10px; font-weight: 700; line-height: 1;
color: #6b7280; background: #f3f4f6; border: 1px solid #d1d5db; border-radius: 50%;
cursor: help; user-select: none;
}
.field-tip-btn:hover, .field-tip-btn:focus { color: #1d4ed8; border-color: #93c5fd; background: #eff6ff; outline: none; }
.field-tip-panel {
position: absolute; z-index: 60; left: 50%; transform: translateX(-50%);
bottom: calc(100% + 6px); width: max-content; max-width: 300px;
padding: 0.35rem 0.5rem; border-radius: 4px;
background: #1f2937; color: #f9fafb; font-size: 11px; font-weight: 500; line-height: 1.35;
text-align: left; white-space: pre-line; box-shadow: 0 2px 8px rgba(0,0,0,.15);
opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .12s, visibility .12s;
}
.field-tip--below .field-tip-panel { bottom: auto; top: calc(100% + 6px); }
.field-tip:hover .field-tip-panel,
.field-tip:focus-within .field-tip-panel { opacity: 1; visibility: visible; }
.flow-lbl-print { display: none; }
@media screen {
.flow-report-wrap { overflow-x: auto; }
.flow-report-table { min-width: 1200px; }
}
@media print {
@page {
size: A4 portrait;
margin: 10mm 8mm;
}
html { font-size: 12px !important; }
.flow-print-sheet {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
}
.print-header,
.print-header table,
.print-header hr {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
}
.print-header table td[style*="width:45%"] table {
width: 160px !important;
max-width: 38% !important;
font-size: 9px !important;
}
.flow-report-wrap {
overflow: hidden !important;
padding: 0 !important;
width: 100% !important;
max-width: 100% !important;
}
.flow-report-table.data-table {
min-width: 0 !important;
width: 100% !important;
max-width: 100% !important;
table-layout: fixed !important;
font-size: 6px !important;
}
.flow-report-table.data-table th,
.flow-report-table.data-table td {
white-space: normal !important;
word-break: keep-all;
overflow-wrap: anywhere;
padding: 1px 1px !important;
line-height: 1.1;
vertical-align: middle;
}
.flow-lbl-screen { display: none !important; }
.flow-lbl-print { display: inline !important; }
/* 세로 A4: 일자 10% + 품목 14% + 수치 12열 각 6.33% ≈ 100% */
.flow-report-table .flow-col-date {
width: 10%;
font-size: 5px !important;
text-align: center;
}
.flow-report-table .flow-col-item {
width: 14%;
text-align: left;
font-size: 5px !important;
padding-top: 3px !important;
padding-bottom: 3px !important;
line-height: 1.25;
}
.flow-report-table .flow-col-num {
width: 6.33%;
white-space: nowrap !important;
font-size: 6px !important;
text-align: right;
padding-left: 0 !important;
padding-right: 1px !important;
}
.flow-report-table thead th {
font-size: 5px !important;
font-weight: 700;
padding: 1px 0 !important;
}
.flow-report-table tbody tr {
break-inside: avoid;
page-break-inside: avoid;
}
}
</style>