From d4af2dc064db6b55f1cafc160fc85e77a06b8f26 Mon Sep 17 00:00:00 2001 From: king Date: Fri, 29 May 2026 03:33:41 +0900 Subject: [PATCH] =?UTF-8?q?ui(expense):=20=EC=B2=A8=EB=B6=80=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=EC=9D=84=20=EB=8F=99=EC=9E=91=20=EC=85=80=EC=97=90=20?= =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EB=85=B8=EC=B6=9C=20(=ED=99=94=EC=82=B4?= =?UTF-8?q?=ED=91=9C=20=ED=86=A0=EA=B8=80=20=EC=A0=9C=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존: 행마다 ▸ 화살표 클릭 → 상세 영역 펼침 → 영수증/기타 추가 변경: 동작 셀에 📎영수증 / 📎기타 버튼 항상 노출. 첨부 목록은 메인 행 바로 아래 인라인으로 항상 표시. 페이지 로드 시 모든 항목의 첨부 자동 fetch. 업로드 input 위치 변경에 맞춰 JS 핸들러 보정. --- .../expense/templates/expense/index.html | 90 +++++++------------ 1 file changed, 34 insertions(+), 56 deletions(-) diff --git a/app/modules/expense/templates/expense/index.html b/app/modules/expense/templates/expense/index.html index 2c759b7..18ce68a 100644 --- a/app/modules/expense/templates/expense/index.html +++ b/app/modules/expense/templates/expense/index.html @@ -84,24 +84,18 @@ - - - + + {% for it in items %} - @@ -127,6 +121,14 @@ {% endif %} {% if supports_workflow %} - - + {% endif %} {% else %} - + {% endfor %}
사용일 분류 수단 가맹점 금액상태동작상태동작
- {% if supports_workflow %} - - {% endif %} - {{ it.spent_at }} {{ it.category }} {{ it.method }} + {% if it.status in ('작성중', '반려') and supports_workflow %} + + + {% endif %} {% if it.status in ('작성중', '반려') %} @@ -139,24 +141,9 @@
-
- 첨부파일 - {% if it.status in ('작성중', '반려') %} -
- - -
- {% endif %} -
  • 불러오는 중…
@@ -170,7 +157,7 @@
등록된 경비가 없습니다.
등록된 경비가 없습니다.
@@ -183,16 +170,10 @@ .erp-page-actions { display: flex; gap: var(--sp-8); margin-bottom: var(--sp-16); flex-wrap: wrap; } - .ex-detail-box { - background: var(--color-ghost-gray); - padding: var(--sp-12) var(--sp-16); - border-radius: 8px; - } - .ex-attach-head { - display: flex; align-items: center; justify-content: space-between; gap: var(--sp-8); - margin-bottom: 8px; - } - .ex-upload { display: flex; gap: 6px; } + /* detail 행은 메인 행 바로 아래 — 위쪽 보더만 살짝, 좌측 들여쓰기 */ + tr.ex-detail > td { padding: 6px var(--sp-16) var(--sp-12) 110px; background: var(--color-canvas-white); } + tr.ex-detail + tr[data-id] > td { border-top: 1px solid var(--color-subtle-ash); } + .ex-detail-box { background: transparent; padding: 0; } .ex-attach-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; } .ex-attach-list li { display: flex; align-items: center; gap: 8px; padding: 6px 8px; @@ -310,23 +291,13 @@ tbody.addEventListener("click", async (e) => { const btn = e.target.closest("button"); if (!btn) return; - const tr = btn.closest("tr[data-id]"); - if (!tr) return; - const id = tr.dataset.id; + // 메인 행 또는 detail 행 모두 처리 + const mainTr = btn.closest("tr[data-id]"); + const detailTr = btn.closest("tr.ex-detail"); + const id = mainTr ? mainTr.dataset.id : (detailTr ? detailTr.dataset.detailFor : null); + if (!id) return; - if (btn.classList.contains("js-toggle")) { - const detail = tbody.querySelector(`tr.ex-detail[data-detail-for="${id}"]`); - if (!detail) return; - const opening = detail.hasAttribute("hidden"); - if (opening) { - detail.removeAttribute("hidden"); - btn.textContent = "▾"; - loadAttachments(id); - } else { - detail.setAttribute("hidden", ""); - btn.textContent = "▸"; - } - } else if (btn.classList.contains("js-edit")) { + if (btn.classList.contains("js-edit")) { const res = await fetch(`/expense/api/items`); if (!res.ok) return; const { items } = await res.json(); @@ -353,13 +324,13 @@ } }); - // 첨부 업로드 + // 첨부 업로드 (메인 행 동작셀 input) tbody.addEventListener("change", async (e) => { const input = e.target.closest("input.js-upload"); if (!input || !input.files.length) return; - const tr = input.closest("tr.ex-detail"); - if (!tr) return; - const itemId = tr.dataset.detailFor; + const mainTr = input.closest("tr[data-id]"); + if (!mainTr) return; + const itemId = mainTr.dataset.id; const fd = new FormData(); fd.append("file", input.files[0]); fd.append("kind", input.dataset.kind || "other"); @@ -375,6 +346,13 @@ } }); + // 페이지 로드 시 모든 detail 행의 첨부 자동 로드 + if (supportsWorkflow) { + tbody.querySelectorAll("tr.ex-detail[data-detail-for]").forEach((tr) => { + loadAttachments(tr.dataset.detailFor); + }); + } + if (!form.spent_at.value) { const d = new Date(); form.spent_at.value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;