CT-01/02/03 공통 컴포넌트 구현 — 페이지네이션/엑셀/인쇄

CT-01: 페이지네이션
- 커스텀 Tailwind 페이저 뷰 (components/pager.php)
- 18개 admin 컨트롤러 findAll() → paginate(20) 전환
- Bag 컨트롤러 7개 리스트도 paginate 적용
- 19개 admin index 뷰에 페이저 링크 추가

CT-02: 엑셀 저장
- export_helper.php (UTF-8 BOM CSV)
- 발주/판매/지정판매소/재고 4개 엑셀 내보내기 라우트+메서드
- 해당 뷰에 "엑셀저장" 버튼 추가

CT-03: 인쇄
- print_header.php (지자체명/제목/결재란 컴포넌트)
- admin/bag 레이아웃에 @media print CSS 추가
- 23개 뷰에 인쇄 버튼 + print_header 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
javamon1174
2026-03-26 16:40:49 +09:00
parent 35561b414b
commit 704141a1f0
49 changed files with 552 additions and 75 deletions

View File

@@ -31,8 +31,7 @@ class Access extends BaseController
{
$start = $this->request->getGet('start');
$end = $this->request->getGet('end');
$builder = $this->memberLogModel->builder();
$builder->select('member_log.*');
$builder = $this->memberLogModel;
$builder->orderBy('mll_regdate', 'DESC');
if ($start !== null && $start !== '') {
$builder->where('mll_regdate >=', $start . ' 00:00:00');
@@ -40,10 +39,11 @@ class Access extends BaseController
if ($end !== null && $end !== '') {
$builder->where('mll_regdate <=', $end . ' 23:59:59');
}
$list = $builder->get()->getResult();
$list = $builder->paginate(20);
$pager = $this->memberLogModel->pager;
return view('admin/layout', [
'title' => '로그인 이력',
'content' => view('admin/access/login_history', ['list' => $list, 'start' => $start, 'end' => $end]),
'content' => view('admin/access/login_history', ['list' => $list, 'start' => $start, 'end' => $end, 'pager' => $pager]),
]);
}
@@ -59,15 +59,14 @@ class Access extends BaseController
$status = MemberApprovalRequestModel::STATUS_PENDING;
}
$builder = $this->approvalModel->builder();
$builder->select(
'member_approval_request.*, member.mb_id, member.mb_name, member.mb_lg_idx, local_government.lg_name'
);
$builder->join('member', 'member.mb_idx = member_approval_request.mb_idx', 'left');
$builder->join('local_government', 'local_government.lg_idx = member.mb_lg_idx', 'left');
$builder->where('member_approval_request.mar_status', $status);
$builder->orderBy('member_approval_request.mar_requested_at', 'DESC');
$list = $builder->get()->getResult();
$list = $this->approvalModel
->select('member_approval_request.*, member.mb_id, member.mb_name, member.mb_lg_idx, local_government.lg_name')
->join('member', 'member.mb_idx = member_approval_request.mb_idx', 'left')
->join('local_government', 'local_government.lg_idx = member.mb_lg_idx', 'left')
->where('member_approval_request.mar_status', $status)
->orderBy('member_approval_request.mar_requested_at', 'DESC')
->paginate(20);
$pager = $this->approvalModel->pager;
return view('admin/layout', [
'title' => '승인 대기',
@@ -75,6 +74,7 @@ class Access extends BaseController
'list' => $list,
'status' => $status,
'roles' => $this->roles,
'pager' => $pager,
]),
]);
}

View File

@@ -13,11 +13,41 @@ class BagInventory extends BaseController
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
$list = model(BagInventoryModel::class)->where('bi_lg_idx', $lgIdx)->orderBy('bi_bag_code', 'ASC')->findAll();
$invModel = model(BagInventoryModel::class);
$list = $invModel->where('bi_lg_idx', $lgIdx)->orderBy('bi_bag_code', 'ASC')->paginate(20);
$pager = $invModel->pager;
return view('admin/layout', [
'title' => '재고 현황',
'content' => view('admin/bag_inventory/index', ['list' => $list]),
'content' => view('admin/bag_inventory/index', ['list' => $list, 'pager' => $pager]),
]);
}
public function export()
{
helper(['admin', 'export']);
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) {
return redirect()->to(site_url('admin/bag-inventory'))->with('error', '지자체를 선택해 주세요.');
}
$list = model(BagInventoryModel::class)->where('bi_lg_idx', $lgIdx)->orderBy('bi_bag_code', 'ASC')->findAll();
$rows = [];
foreach ($list as $row) {
$rows[] = [
$row->bi_idx,
$row->bi_bag_code,
$row->bi_bag_name,
(int) $row->bi_qty,
$row->bi_updated_at,
];
}
export_csv(
'재고현황_' . date('Ymd') . '.csv',
['번호', '봉투코드', '봉투명', '현재재고(낱장)', '최종갱신'],
$rows
);
}
}

View File

@@ -29,11 +29,12 @@ class BagIssue extends BaseController
if ($startDate) $builder->where('bi2_issue_date >=', $startDate);
if ($endDate) $builder->where('bi2_issue_date <=', $endDate);
$list = $builder->orderBy('bi2_issue_date', 'DESC')->orderBy('bi2_idx', 'DESC')->findAll();
$list = $builder->orderBy('bi2_issue_date', 'DESC')->orderBy('bi2_idx', 'DESC')->paginate(20);
$pager = $this->issueModel->pager;
return view('admin/layout', [
'title' => '무료용 불출 관리',
'content' => view('admin/bag_issue/index', compact('list', 'startDate', 'endDate')),
'content' => view('admin/bag_issue/index', compact('list', 'startDate', 'endDate', 'pager')),
]);
}

View File

@@ -42,7 +42,8 @@ class BagOrder extends BaseController
if ($endDate) $builder->where('bo_order_date <=', $endDate);
if ($status) $builder->where('bo_status', $status);
$list = $builder->orderBy('bo_order_date', 'DESC')->orderBy('bo_idx', 'DESC')->findAll();
$list = $builder->orderBy('bo_order_date', 'DESC')->orderBy('bo_idx', 'DESC')->paginate(20);
$pager = $this->orderModel->pager;
// 발주별 품목 합계
$itemSummary = [];
@@ -60,10 +61,56 @@ class BagOrder extends BaseController
return view('admin/layout', [
'title' => '발주 현황',
'content' => view('admin/bag_order/index', compact('list', 'itemSummary', 'companyMap', 'agencyMap', 'startDate', 'endDate', 'status')),
'content' => view('admin/bag_order/index', compact('list', 'itemSummary', 'companyMap', 'agencyMap', 'startDate', 'endDate', 'status', 'pager')),
]);
}
public function export()
{
helper(['admin', 'export']);
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) {
return redirect()->to(site_url('admin/bag-orders'))->with('error', '지자체를 선택해 주세요.');
}
$builder = $this->orderModel->where('bo_lg_idx', $lgIdx);
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$status = $this->request->getGet('status');
if ($startDate) $builder->where('bo_order_date >=', $startDate);
if ($endDate) $builder->where('bo_order_date <=', $endDate);
if ($status) $builder->where('bo_status', $status);
$list = $builder->orderBy('bo_order_date', 'DESC')->orderBy('bo_idx', 'DESC')->findAll();
$rows = [];
$statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제'];
foreach ($list as $row) {
$items = $this->itemModel->where('boi_bo_idx', $row->bo_idx)->findAll();
$totalQty = 0;
$totalAmt = 0;
foreach ($items as $it) {
$totalQty += (int) $it->boi_qty_sheet;
$totalAmt += (float) $it->boi_amount;
}
$rows[] = [
$row->bo_idx,
$row->bo_lot_no,
$row->bo_order_date,
count($items),
$totalQty,
$totalAmt,
$statusMap[$row->bo_status] ?? $row->bo_status,
];
}
export_csv(
'발주현황_' . date('Ymd') . '.csv',
['번호', 'LOT번호', '발주일', '품목수', '총수량', '총금액', '상태'],
$rows
);
}
public function create()
{
helper('admin');

View File

@@ -42,7 +42,8 @@ class BagPrice extends BaseController
->groupEnd();
}
$list = $builder->orderBy('bp_bag_code', 'ASC')->orderBy('bp_start_date', 'DESC')->findAll();
$list = $builder->orderBy('bp_bag_code', 'ASC')->orderBy('bp_start_date', 'DESC')->paginate(20);
$pager = $this->priceModel->pager;
return view('admin/layout', [
'title' => '봉투 단가 관리',
@@ -50,6 +51,7 @@ class BagPrice extends BaseController
'list' => $list,
'startDate' => $startDate,
'endDate' => $endDate,
'pager' => $pager,
]),
]);
}

View File

@@ -30,11 +30,12 @@ class BagReceiving extends BaseController
if ($startDate) $builder->where('br_receive_date >=', $startDate);
if ($endDate) $builder->where('br_receive_date <=', $endDate);
$list = $builder->orderBy('br_receive_date', 'DESC')->orderBy('br_idx', 'DESC')->findAll();
$list = $builder->orderBy('br_receive_date', 'DESC')->orderBy('br_idx', 'DESC')->paginate(20);
$pager = $this->recvModel->pager;
return view('admin/layout', [
'title' => '입고 현황',
'content' => view('admin/bag_receiving/index', compact('list', 'startDate', 'endDate')),
'content' => view('admin/bag_receiving/index', compact('list', 'startDate', 'endDate', 'pager')),
]);
}

View File

@@ -33,14 +33,56 @@ class BagSale extends BaseController
if ($endDate) $builder->where('bs_sale_date <=', $endDate);
if ($type) $builder->where('bs_type', $type);
$list = $builder->orderBy('bs_sale_date', 'DESC')->orderBy('bs_idx', 'DESC')->findAll();
$list = $builder->orderBy('bs_sale_date', 'DESC')->orderBy('bs_idx', 'DESC')->paginate(20);
$pager = $this->saleModel->pager;
return view('admin/layout', [
'title' => '판매/반품 관리',
'content' => view('admin/bag_sale/index', compact('list', 'startDate', 'endDate', 'type')),
'content' => view('admin/bag_sale/index', compact('list', 'startDate', 'endDate', 'type', 'pager')),
]);
}
public function export()
{
helper(['admin', 'export']);
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) {
return redirect()->to(site_url('admin/bag-sales'))->with('error', '지자체를 선택해 주세요.');
}
$builder = $this->saleModel->where('bs_lg_idx', $lgIdx);
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$type = $this->request->getGet('type');
if ($startDate) $builder->where('bs_sale_date >=', $startDate);
if ($endDate) $builder->where('bs_sale_date <=', $endDate);
if ($type) $builder->where('bs_type', $type);
$list = $builder->orderBy('bs_sale_date', 'DESC')->orderBy('bs_idx', 'DESC')->findAll();
$typeMap = ['sale' => '판매', 'return' => '반품', 'cancel' => '취소'];
$rows = [];
foreach ($list as $row) {
$rows[] = [
$row->bs_idx,
$row->bs_ds_name,
$row->bs_sale_date,
$row->bs_bag_code,
$row->bs_bag_name,
(int) $row->bs_qty,
(int) $row->bs_unit_price,
(int) $row->bs_amount,
$typeMap[$row->bs_type] ?? $row->bs_type,
];
}
export_csv(
'판매반품_' . date('Ymd') . '.csv',
['번호', '판매소', '판매일', '봉투코드', '봉투명', '수량', '단가', '금액', '구분'],
$rows
);
}
public function create()
{
helper('admin');

View File

@@ -24,13 +24,15 @@ class CodeDetail extends BaseController
return redirect()->to(site_url('admin/code-kinds'))->with('error', '코드 종류를 찾을 수 없습니다.');
}
$list = $this->detailModel->getByKind($ckIdx);
$list = $this->detailModel->where('cd_ck_idx', $ckIdx)->orderBy('cd_sort', 'ASC')->paginate(20);
$pager = $this->detailModel->pager;
return view('admin/layout', [
'title' => '세부코드 관리 — ' . $kind->ck_name . ' (' . $kind->ck_code . ')',
'content' => view('admin/code_detail/index', [
'kind' => $kind,
'list' => $list,
'kind' => $kind,
'list' => $list,
'pager' => $pager,
]),
]);
}

View File

@@ -18,7 +18,8 @@ class CodeKind extends BaseController
public function index()
{
$list = $this->kindModel->orderBy('ck_code', 'ASC')->findAll();
$list = $this->kindModel->orderBy('ck_code', 'ASC')->paginate(20);
$pager = $this->kindModel->pager;
// 세부코드 수 매핑
$detailModel = model(CodeDetailModel::class);
@@ -32,6 +33,7 @@ class CodeKind extends BaseController
'content' => view('admin/code_kind/index', [
'list' => $list,
'countMap' => $countMap,
'pager' => $pager,
]),
]);
}

View File

@@ -22,11 +22,12 @@ class Company extends BaseController
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
}
$list = $this->model->where('cp_lg_idx', $lgIdx)->orderBy('cp_idx', 'DESC')->findAll();
$list = $this->model->where('cp_lg_idx', $lgIdx)->orderBy('cp_idx', 'DESC')->paginate(20);
$pager = $this->model->pager;
return view('admin/layout', [
'title' => '업체 관리',
'content' => view('admin/company/index', ['list' => $list]),
'content' => view('admin/company/index', ['list' => $list, 'pager' => $pager]),
]);
}

View File

@@ -46,7 +46,8 @@ class DesignatedShop extends BaseController
$list = $this->shopModel
->where('ds_lg_idx', $lgIdx)
->orderBy('ds_idx', 'DESC')
->findAll();
->paginate(20);
$pager = $this->shopModel->pager;
// 지자체 이름 매핑용
$lgMap = [];
@@ -59,10 +60,45 @@ class DesignatedShop extends BaseController
'content' => view('admin/designated_shop/index', [
'list' => $list,
'lgMap' => $lgMap,
'pager' => $pager,
]),
]);
}
public function export()
{
helper(['admin', 'export']);
$lgIdx = admin_effective_lg_idx();
if (!$lgIdx) {
return redirect()->to(site_url('admin/designated-shops'))->with('error', '지자체를 선택해 주세요.');
}
$list = $this->shopModel->where('ds_lg_idx', $lgIdx)->orderBy('ds_idx', 'DESC')->findAll();
$rows = [];
foreach ($list as $row) {
$stateMap = [1 => '정상', 2 => '폐업', 3 => '직권해지'];
$rows[] = [
$row->ds_idx,
$row->ds_shop_no,
$row->ds_name,
$row->ds_rep_name,
$row->ds_biz_no,
$row->ds_va_number,
$row->ds_tel ?? '',
$row->ds_addr ?? '',
$stateMap[(int) $row->ds_state] ?? '',
$row->ds_regdate ?? '',
];
}
export_csv(
'지정판매소_' . date('Ymd') . '.csv',
['번호', '판매소번호', '상호명', '대표자', '사업자번호', '가상계좌', '전화번호', '주소', '상태', '등록일'],
$rows
);
}
/**
* 지정판매소 등록 폼 (효과 지자체 기준)
*/

View File

@@ -30,11 +30,12 @@ class FreeRecipient extends BaseController
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
}
$list = $this->model->where('fr_lg_idx', $lgIdx)->orderBy('fr_idx', 'DESC')->findAll();
$list = $this->model->where('fr_lg_idx', $lgIdx)->orderBy('fr_idx', 'DESC')->paginate(20);
$pager = $this->model->pager;
return view('admin/layout', [
'title' => '무료용 대상자 관리',
'content' => view('admin/free_recipient/index', ['list' => $list]),
'content' => view('admin/free_recipient/index', ['list' => $list, 'pager' => $pager]),
]);
}

View File

@@ -32,11 +32,12 @@ class LocalGovernment extends BaseController
->with('error', '지자체 관리는 상위 관리자만 접근할 수 있습니다.');
}
$list = $this->lgModel->orderBy('lg_idx', 'DESC')->findAll();
$list = $this->lgModel->orderBy('lg_idx', 'DESC')->paginate(20);
$pager = $this->lgModel->pager;
return view('admin/layout', [
'title' => '지자체 관리',
'content' => view('admin/local_government/index', ['list' => $list]),
'content' => view('admin/local_government/index', ['list' => $list, 'pager' => $pager]),
]);
}

View File

@@ -30,11 +30,12 @@ class Manager extends BaseController
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
}
$list = $this->model->where('mg_lg_idx', $lgIdx)->orderBy('mg_idx', 'DESC')->findAll();
$list = $this->model->where('mg_lg_idx', $lgIdx)->orderBy('mg_idx', 'DESC')->paginate(20);
$pager = $this->model->pager;
return view('admin/layout', [
'title' => '담당자 관리',
'content' => view('admin/manager/index', ['list' => $list]),
'content' => view('admin/manager/index', ['list' => $list, 'pager' => $pager]),
]);
}

View File

@@ -38,12 +38,13 @@ class PackagingUnit extends BaseController
$builder->groupStart()->where('pu_end_date IS NULL')->orWhere('pu_end_date <=', $endDate)->groupEnd();
}
$list = $builder->orderBy('pu_bag_code', 'ASC')->orderBy('pu_start_date', 'DESC')->findAll();
$list = $builder->orderBy('pu_bag_code', 'ASC')->orderBy('pu_start_date', 'DESC')->paginate(20);
$pager = $this->unitModel->pager;
return view('admin/layout', [
'title' => '포장 단위 관리',
'content' => view('admin/packaging_unit/index', [
'list' => $list, 'startDate' => $startDate, 'endDate' => $endDate,
'list' => $list, 'startDate' => $startDate, 'endDate' => $endDate, 'pager' => $pager,
]),
]);
}

View File

@@ -22,11 +22,12 @@ class SalesAgency extends BaseController
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.');
}
$list = $this->model->where('sa_lg_idx', $lgIdx)->orderBy('sa_idx', 'DESC')->findAll();
$list = $this->model->where('sa_lg_idx', $lgIdx)->orderBy('sa_idx', 'DESC')->paginate(20);
$pager = $this->model->pager;
return view('admin/layout', [
'title' => '판매 대행소 관리',
'content' => view('admin/sales_agency/index', ['list' => $list]),
'content' => view('admin/sales_agency/index', ['list' => $list, 'pager' => $pager]),
]);
}

View File

@@ -34,11 +34,12 @@ class ShopOrder extends BaseController
if ($startDate) $builder->where('so_delivery_date >=', $startDate);
if ($endDate) $builder->where('so_delivery_date <=', $endDate);
$list = $builder->orderBy('so_idx', 'DESC')->findAll();
$list = $builder->orderBy('so_idx', 'DESC')->paginate(20);
$pager = $this->orderModel->pager;
return view('admin/layout', [
'title' => '주문 접수 관리',
'content' => view('admin/shop_order/index', compact('list', 'startDate', 'endDate')),
'content' => view('admin/shop_order/index', compact('list', 'startDate', 'endDate', 'pager')),
]);
}

View File

@@ -26,7 +26,8 @@ class User extends BaseController
*/
public function index(): string
{
$list = $this->memberModel->orderBy('mb_idx', 'DESC')->findAll();
$list = $this->memberModel->orderBy('mb_idx', 'DESC')->paginate(20);
$pager = $this->memberModel->pager;
$approvalMap = [];
try {
$memberIds = array_map(static fn ($row) => (int) $row->mb_idx, $list);
@@ -56,6 +57,7 @@ class User extends BaseController
'list' => $list,
'roles' => $this->roles,
'approvalMap' => $approvalMap,
'pager' => $pager,
]),
]);
}