diff --git a/Assets/Resources/EWLK/UIToolkit/Main/EWLKMfgOrderModalContentUss.uss b/Assets/Resources/EWLK/UIToolkit/Main/EWLKMfgOrderModalContentUss.uss
index 9ca94d28..e37fc811 100644
--- a/Assets/Resources/EWLK/UIToolkit/Main/EWLKMfgOrderModalContentUss.uss
+++ b/Assets/Resources/EWLK/UIToolkit/Main/EWLKMfgOrderModalContentUss.uss
@@ -1,206 +1,254 @@
-/* ──────────────────────────────────────────────────────────
- EWLKMfgOrderModalContent — 제조지시현황 모달
- ────────────────────────────────────────────────────────── */
+/* ============================================================
+ EWLKMfgOrderModalContent — 제조지시현황 (라이트 테마)
+ 탭 + 요약 바 + 11컬럼 테이블
+ ============================================================ */
-/* ── 루트 컨테이너 ──────────────────────────────────────── */
+/* ── 루트 ──────────────────────────────────────────────── */
.ewlk-mfgorder {
- flex-direction: column;
flex-grow: 1;
- background-color: rgb(18, 22, 36);
- padding: 0;
- min-height: 400px;
+ flex-direction: column;
+ background-color: rgb(255, 255, 255);
}
-/* ── 탭 바 ──────────────────────────────────────────────── */
+/* ── 탭 바 ─────────────────────────────────────────────── */
.ewlk-mfgorder__tab-bar {
flex-direction: row;
- border-bottom-width: 2px;
- border-bottom-color: rgb(50, 60, 90);
- margin-bottom: 0;
+ padding-left: 12px;
+ padding-top: 8px;
}
.ewlk-mfgorder__tab {
- flex-shrink: 0;
- padding: 8px 20px;
- background-color: rgb(34, 42, 66);
- color: rgb(140, 150, 175);
+ min-width: 0;
+ padding: 6px 16px;
+ margin-right: 4px;
+ background-color: rgba(0, 0, 0, 0);
border-width: 0;
border-bottom-width: 2px;
- border-bottom-color: rgba(0, 0, 0, 0);
+ border-color: rgba(0, 0, 0, 0);
border-radius: 0;
- font-size: 13px;
- -unity-font-style: normal;
- margin-bottom: -2px;
-}
-
-.ewlk-mfgorder__tab:hover {
- background-color: rgb(40, 50, 78);
- color: rgb(220, 225, 240);
+ font-size: 12px;
+ color: rgb(120, 120, 120);
}
.ewlk-mfgorder__tab--active {
- color: rgb(220, 225, 240);
- border-bottom-color: rgb(0, 140, 255);
+ color: rgb(30, 100, 220);
+ border-bottom-color: rgb(30, 100, 220);
-unity-font-style: bold;
}
-/* ── 요약 바 ─────────────────────────────────────────────── */
+.ewlk-mfgorder__tab:hover {
+ color: rgb(60, 60, 60);
+}
+
+/* ── 요약 바 ────────────────────────────────────────────── */
.ewlk-mfgorder__summary-bar {
flex-direction: row;
- background-color: rgb(26, 32, 50);
- padding: 10px 16px;
- border-bottom-width: 1px;
- border-bottom-color: rgb(50, 60, 90);
+ padding: 12px 16px;
+ border-width: 1px;
+ border-color: rgb(230, 230, 230);
+ border-radius: 4px;
+ margin: 8px 12px;
}
-.ewlk-mfgorder__summary-item {
+.ewlk-mfgorder__summary-left {
+ flex-grow: 1;
+ flex-direction: column;
+ padding-right: 24px;
+ border-right-width: 1px;
+ border-color: rgb(230, 230, 230);
+}
+
+.ewlk-mfgorder__summary-right {
+ flex-grow: 1;
+ flex-direction: column;
+ padding-left: 24px;
+}
+
+/* ── 바 아이템 (가로: 라벨 | 값 | 프로그레스 바) ──────── */
+.ewlk-mfgorder__bar-item {
flex-direction: row;
align-items: center;
- flex-grow: 1;
- margin-right: 16px;
+ margin-bottom: 8px;
}
-.ewlk-mfgorder__summary-title {
- color: rgb(140, 150, 175);
+.ewlk-mfgorder__bar-item-title {
font-size: 11px;
- margin-right: 8px;
+ color: rgb(100, 100, 100);
+ width: 110px;
flex-shrink: 0;
+ -unity-text-align: middle-left;
}
-.ewlk-mfgorder__summary-value {
- color: rgb(220, 225, 240);
+.ewlk-mfgorder__bar-item-value {
font-size: 13px;
-unity-font-style: bold;
+ color: rgb(33, 33, 33);
+ width: 120px;
flex-shrink: 0;
+ -unity-text-align: middle-left;
}
-/* ── 설비가동률 행 ───────────────────────────────────────── */
-.ewlk-mfgorder__equip-row {
- flex-direction: row;
- align-items: center;
- background-color: rgb(26, 32, 50);
- padding: 6px 16px;
- border-bottom-width: 1px;
- border-bottom-color: rgb(50, 60, 90);
+.ewlk-mfgorder__bar-item .ewlk-mfgorder__progress-wrap {
+ flex-grow: 1;
}
-.ewlk-mfgorder__equip-title {
- color: rgb(140, 150, 175);
- font-size: 11px;
+/* ── 설비 가동율 값 세로 스택 (%+대수) ──────────────────── */
+.ewlk-mfgorder__equip-value-stack {
+ flex-direction: column;
+ width: 120px;
flex-shrink: 0;
- margin-right: 10px;
- width: 180px;
-}
-
-.ewlk-mfgorder__equip-rate {
- color: rgb(220, 225, 240);
- font-size: 12px;
- flex-shrink: 0;
- margin-left: 8px;
- width: 44px;
}
.ewlk-mfgorder__count {
- color: rgb(140, 150, 175);
- font-size: 12px;
- flex-shrink: 0;
- margin-left: 12px;
+ font-size: 11px;
+ color: rgb(30, 100, 220);
+ -unity-font-style: bold;
+ -unity-text-align: middle-left;
}
-/* ── 프로그레스 바 (공통) ────────────────────────────────── */
+/* ── 검색창 ────────────────────────────────────────────── */
+.ewlk-mfgorder__search-row {
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-end;
+ margin: 4px 12px 8px 12px;
+}
+
+.ewlk-mfgorder__search-icon {
+ color: rgb(180, 180, 180);
+ margin-right: 4px;
+ flex-shrink: 0;
+}
+
+.ewlk-mfgorder__search-field {
+ width: 25%;
+ border-width: 0;
+ background-color: rgba(0, 0, 0, 0);
+ font-size: 12px;
+ color: rgb(160, 160, 160);
+}
+
+/* 검색창 다크 테마 오버라이드 (3단계 specificity) */
+.ewlk-mfgorder .ewlk-mfgorder__search-row .ewlk-mfgorder__search-field .unity-text-input {
+ border-width: 0;
+ padding: 2px 0;
+ background-color: rgb(255, 255, 255);
+ color: rgb(30, 30, 30);
+ --unity-cursor-color: rgb(80, 80, 80);
+}
+
+/* placeholder 상태 — 텍스트 회색 */
+.ewlk-mfgorder .ewlk-mfgorder__search-row .ewlk-mfgorder__search-field--placeholder .unity-text-input {
+ color: rgb(160, 160, 160);
+}
+
+/* ── 프로그레스 바 공통 ──────────────────────────────────── */
.ewlk-mfgorder__progress-wrap {
- flex-grow: 1;
- height: 10px;
- background-color: rgb(50, 60, 90);
- border-radius: 4px;
+ height: 12px;
+ background-color: rgb(240, 240, 240);
+ border-radius: 2px;
overflow: hidden;
- max-width: 300px;
}
.ewlk-mfgorder__progress-fill {
height: 100%;
- background-color: rgb(0, 200, 100);
- border-radius: 4px;
- width: 0%;
+ background-color: rgb(180, 200, 230);
+ border-radius: 2px;
+ width: 0;
}
-/* ── 테이블 헤더 ─────────────────────────────────────────── */
+/* ── 테이블 헤더 ──────────────────────────────────────── */
.ewlk-mfgorder__header {
flex-direction: row;
- background-color: rgb(34, 42, 66);
+ min-height: 32px;
+ background-color: rgb(245, 245, 250);
border-bottom-width: 1px;
- border-bottom-color: rgb(50, 60, 90);
- padding: 0 4px;
+ border-color: rgb(220, 220, 220);
+ margin-left: 12px;
+ margin-right: 12px;
}
.ewlk-mfgorder__header-cell {
- color: rgb(140, 150, 175);
+ -unity-text-align: middle-center;
font-size: 11px;
-unity-font-style: bold;
- -unity-text-align: middle-center;
- padding: 6px 4px;
+ color: rgb(80, 80, 80);
+ padding: 4px 2px;
border-right-width: 1px;
- border-right-color: rgb(50, 60, 90);
+ border-color: rgb(230, 230, 230);
}
-/* ── ListView ────────────────────────────────────────────── */
+.ewlk-mfgorder__header-cell:last-child {
+ border-right-width: 0;
+}
+
+/* ── ListView ──────────────────────────────────────────── */
.ewlk-mfgorder__list {
flex-grow: 1;
- background-color: rgb(18, 22, 36);
+ margin-left: 12px;
+ margin-right: 12px;
}
-/* ── 데이터 행 ───────────────────────────────────────────── */
+/* ── 데이터 행 ─────────────────────────────────────────── */
.ewlk-mfgorder__row {
flex-direction: row;
- align-items: center;
- height: 28px;
- padding: 0 4px;
+ min-height: 28px;
border-bottom-width: 1px;
- border-bottom-color: rgb(50, 60, 90);
+ border-color: rgb(240, 240, 240);
}
+/* ── 셀 공통 ──────────────────────────────────────────── */
.ewlk-mfgorder__cell {
- height: 100%;
justify-content: center;
- padding: 0 4px;
+ align-items: center;
+ padding: 2px 4px;
border-right-width: 1px;
- border-right-color: rgb(50, 60, 90);
- overflow: hidden;
+ border-color: rgb(245, 245, 245);
+}
+
+.ewlk-mfgorder__cell:last-child {
+ border-right-width: 0;
}
.ewlk-mfgorder__cell-label {
- color: rgb(220, 225, 240);
- font-size: 12px;
- -unity-text-align: middle-left;
- overflow: hidden;
-}
-
-/* ── 달성률 셀 내부 ─────────────────────────────────────── */
-.ewlk-mfgorder__stopped {
- color: rgb(160, 165, 185);
font-size: 11px;
+ color: rgb(50, 50, 50);
-unity-text-align: middle-center;
- background-color: rgb(60, 65, 85);
- padding: 2px 6px;
- border-radius: 3px;
- display: none;
+ overflow: hidden;
+ white-space: nowrap;
}
+/* 품목명 줄임표시 */
+.ewlk-mfgorder__cell-label--ellipsis {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+/* ── 진척률 셀 ────────────────────────────────────────── */
.ewlk-mfgorder__pct {
- color: rgb(220, 225, 240);
font-size: 11px;
- -unity-text-align: middle-right;
+ color: rgb(50, 50, 50);
+ -unity-text-align: middle-center;
+ min-width: 30px;
flex-shrink: 0;
- margin-left: 4px;
- width: 36px;
- display: none;
}
-/* ── 컬럼 너비 ───────────────────────────────────────────── */
-.col-equip { width: 100px; flex-shrink: 0; }
-.col-order { width: 120px; flex-shrink: 0; }
-.col-item-code { width: 110px; flex-shrink: 0; }
-.col-item-name { flex-grow: 1; }
-.col-time { width: 80px; flex-shrink: 0; }
-.col-target { width: 90px; flex-shrink: 0; }
-.col-achievement { width: 150px; flex-shrink: 0; flex-direction: row; align-items: center; }
+/* ── 컬럼 너비 ────────────────────────────────────────── */
+.col-no { width: 35px; flex-shrink: 0; }
+.col-equip { width: 110px; flex-shrink: 0; }
+.col-order { width: 100px; flex-shrink: 0; }
+.col-item-code { width: 100px; flex-shrink: 0; }
+.col-item-name { width: 90px; flex-shrink: 0; }
+.col-start-time { width: 90px; flex-shrink: 0; }
+.col-end-time { width: 90px; flex-shrink: 0; }
+.col-work-time { width: 80px; flex-shrink: 0; }
+.col-plan-prod { width: 90px; flex-shrink: 0; }
+.col-actual-prod { width: 80px; flex-shrink: 0; }
+.col-achievement { flex-grow: 1; flex-direction: row; align-items: center; padding-left: 4px; padding-right: 4px; }
+
+/* 진척률 셀 내부 프로그레스 바 */
+.col-achievement .ewlk-mfgorder__progress-wrap {
+ flex-grow: 1;
+ margin-right: 4px;
+}
diff --git a/Assets/Resources/EWLK/UIToolkit/Main/EWLKOverViewModalContentUss.uss b/Assets/Resources/EWLK/UIToolkit/Main/EWLKOverViewModalContentUss.uss
index 096b69b9..5b3c63eb 100644
--- a/Assets/Resources/EWLK/UIToolkit/Main/EWLKOverViewModalContentUss.uss
+++ b/Assets/Resources/EWLK/UIToolkit/Main/EWLKOverViewModalContentUss.uss
@@ -1,128 +1,150 @@
/* ============================================================
- EWLKOverViewModalContent — OverView 모달 테이블 스타일
-
- 레이아웃 구조:
- .ewlk-overview (테이블 루트, flex-column)
- .ewlk-overview__header (헤더 행, flex-row)
- .ewlk-overview__header-cell (헤더 셀)
- .ewlk-overview__group × 3 (작업장 그룹, flex-row)
- .ewlk-overview__cell--name (작업장 이름, 2행 span)
- .ewlk-overview__rows (월간+일간, flex-column)
- .ewlk-overview__row × 2 (데이터 행, flex-row)
- .ewlk-overview__cell--period
- .ewlk-overview__cell--data × 3
- .ewlk-overview__cell--type
+ EWLKOverViewModalContent — OverView 컨텐츠 스타일
+ 3개 컬럼 (작업장별), 각 컬럼에 월간/일간 섹션 + 프로그레스 바
============================================================ */
-/* ── 테이블 루트 ─────────────────────────────────────────── */
+/* ── 루트 ──────────────────────────────────────────────── */
.ewlk-overview {
- flex-direction: column;
+ flex-grow: 1;
background-color: rgb(255, 255, 255);
- border-width: 1px;
- border-color: rgb(140, 140, 140);
+ padding: 16px;
}
-/* ── 헤더 행 ─────────────────────────────────────────────── */
-.ewlk-overview__header {
+/* ── 3컬럼 컨테이너 ────────────────────────────────────── */
+.ewlk-overview__columns {
flex-direction: row;
- min-height: 36px;
- background-color: rgb(200, 206, 224);
- border-bottom-width: 1px;
- border-color: rgb(140, 140, 140);
-}
-
-/* ── 헤더 셀 공통 ────────────────────────────────────────── */
-.ewlk-overview__header-cell {
- -unity-text-align: middle-center;
- font-size: 12px;
- -unity-font-style: bold;
- color: rgb(20, 20, 20);
- padding-top: 4px;
- padding-bottom: 4px;
- border-right-width: 1px;
- border-color: rgb(140, 140, 140);
-}
-
-/* ── 작업장 그룹 ─────────────────────────────────────────── */
-.ewlk-overview__group {
- flex-direction: row;
- border-bottom-width: 1px;
- border-color: rgb(140, 140, 140);
-}
-
-/* ── 행 묶음 컨테이너 ────────────────────────────────────── */
-.ewlk-overview__rows {
flex-grow: 1;
+ align-items: stretch;
+}
+
+/* ── 작업장 컬럼 ────────────────────────────────────────── */
+.ewlk-overview__column {
+ flex-grow: 1;
+ flex-basis: 0;
flex-direction: column;
+ border-width: 1px;
+ border-color: rgb(230, 230, 230);
+ border-radius: 4px;
+ margin-right: 12px;
+ padding: 12px;
+ background-color: rgb(252, 252, 252);
}
-/* ── 데이터 행 ───────────────────────────────────────────── */
-.ewlk-overview__row {
- flex-direction: row;
- min-height: 36px;
- border-bottom-width: 1px;
- border-color: rgb(200, 200, 200);
+.ewlk-overview__column:last-child {
+ margin-right: 0;
}
-.ewlk-overview__rows .ewlk-overview__row:last-child {
- border-bottom-width: 0;
-}
-
-/* ── 공통 셀 ─────────────────────────────────────────────── */
-.ewlk-overview__cell {
- -unity-text-align: middle-center;
- font-size: 12px;
- color: rgb(20, 20, 20);
- padding-left: 6px;
- padding-right: 6px;
- align-items: center;
- justify-content: center;
- border-right-width: 1px;
- border-color: rgb(200, 200, 200);
-}
-
-/* ── 셀 종류별 너비 / 배경 ──────────────────────────────── */
-
-/* 작업장 이름 (헤더의 '구분' + 그룹의 작업장명 — 동일 너비) */
-.ewlk-overview__cell--name {
- width: 140px;
- flex-shrink: 0;
+/* ── 컬럼 타이틀 ────────────────────────────────────────── */
+.ewlk-overview__column-title {
+ font-size: 13px;
-unity-font-style: bold;
- background-color: rgb(232, 235, 248);
- border-right-color: rgb(140, 140, 140);
+ color: rgb(33, 33, 33);
+ -unity-text-align: middle-center;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom-width: 1px;
+ border-color: rgb(220, 220, 220);
}
-/* 월간/일간 구분 열 */
-.ewlk-overview__cell--period {
- width: 50px;
- flex-shrink: 0;
- background-color: rgb(245, 246, 252);
- border-right-color: rgb(180, 180, 180);
-}
-
-/* 목표수량·현시점계획·실적수량 (동등 너비 분할) */
-.ewlk-overview__cell--data {
+/* ── 기간 섹션 (월간/일간) — 각각 50% 높이 ──────────────── */
+.ewlk-overview__section {
+ flex-direction: column;
flex-grow: 1;
- flex-shrink: 1;
+ flex-basis: 0;
}
-/* 구분 (제조/생산) — 마지막 열, 오른쪽 테두리 제거 */
-.ewlk-overview__cell--type {
- width: 60px;
+/* ── 기간 라벨 ──────────────────────────────────────────── */
+.ewlk-overview__period-label {
+ font-size: 12px;
+ -unity-font-style: bold;
+ color: rgb(100, 100, 100);
+ margin-bottom: 4px;
+ -unity-text-align: middle-center;
+}
+
+/* ── 섹션 내 구분선 ──────────────────────────────────────── */
+.ewlk-overview__divider {
+ height: 1px;
+ background-color: rgb(230, 230, 230);
+ margin-bottom: 8px;
+}
+
+/* ── 데이터 행 (가로: 라벨 | 값 | 프로그레스 바) ──────── */
+.ewlk-overview__data-row {
+ flex-direction: row;
+ align-items: center;
+ margin-bottom: 6px;
+}
+
+/* ── 라벨 + 값 영역 ──────────────────────────────────── */
+.ewlk-overview__label-row {
+ flex-direction: row;
+ align-items: center;
flex-shrink: 0;
- border-right-width: 0;
+ width: 55%;
}
-/* 헤더의 name/period/type 셀도 동일 너비 클래스 공유 */
-.ewlk-overview__header .ewlk-overview__cell--name {
- background-color: rgb(200, 206, 224);
- border-right-color: rgb(140, 140, 140);
+.ewlk-overview__data-name {
+ font-size: 11px;
+ color: rgb(100, 100, 100);
+ width: 80px;
+ flex-shrink: 0;
+ -unity-text-align: middle-left;
}
-.ewlk-overview__header .ewlk-overview__cell--period {
- background-color: rgb(200, 206, 224);
+.ewlk-overview__data-value {
+ font-size: 12px;
+ -unity-font-style: bold;
+ color: rgb(33, 33, 33);
+ flex-grow: 1;
+ -unity-text-align: middle-left;
}
-.ewlk-overview__header .ewlk-overview__cell--type {
- border-right-width: 0;
+/* 목표수량 값 — 검정 */
+.ewlk-overview__value--target {
+ color: rgb(33, 33, 33);
+}
+
+/* 현시점 계획 값 — 파랑 */
+.ewlk-overview__value--plan {
+ color: rgb(40, 100, 200);
+}
+
+/* 실적수량 값 — 빨강 */
+.ewlk-overview__value--actual {
+ color: rgb(200, 60, 50);
+}
+
+/* ── 프로그레스 바 배경 ──────────────────────────────────── */
+.ewlk-overview__bar-bg {
+ flex-grow: 1;
+ height: 14px;
+ background-color: rgb(240, 240, 240);
+ border-radius: 2px;
+ overflow: hidden;
+}
+
+/* ── 프로그레스 바 채움 ──────────────────────────────────── */
+.ewlk-overview__bar-fill {
+ height: 100%;
+ border-radius: 2px;
+ width: 0;
+ transition-property: width;
+ transition-duration: 0.3s;
+ transition-timing-function: ease-out;
+}
+
+/* 목표수량 — 연한 회색 */
+.ewlk-overview__bar--target {
+ background-color: rgb(200, 200, 210);
+}
+
+/* 현시점 계획 — 연한 파랑 */
+.ewlk-overview__bar--plan {
+ background-color: rgb(180, 200, 230);
+}
+
+/* 실적수량 — 연한 빨강 */
+.ewlk-overview__bar--actual {
+ background-color: rgb(230, 190, 185);
}
diff --git a/Assets/Resources/EWLK/UIToolkit/Main/EWLKWorkExplorerContentUss.uss b/Assets/Resources/EWLK/UIToolkit/Main/EWLKWorkExplorerContentUss.uss
index a9f0adf8..46164cdf 100644
--- a/Assets/Resources/EWLK/UIToolkit/Main/EWLKWorkExplorerContentUss.uss
+++ b/Assets/Resources/EWLK/UIToolkit/Main/EWLKWorkExplorerContentUss.uss
@@ -4,7 +4,10 @@
============================================================ */
.ewlk-work-explorer {
+ flex-grow: 1;
+ flex-direction: column;
background-color: rgb(255, 255, 255);
+ padding: 12px 16px;
}
/* ── 섹션 타이틀 ──────────────────────────────────────── */
diff --git a/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderData.cs b/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderData.cs
index 2646ebdd..4d7d2449 100644
--- a/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderData.cs
+++ b/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderData.cs
@@ -3,82 +3,94 @@ using System.Collections.Generic;
namespace UVC.EnglewoodLAB.Data
{
- /// 제조지시현황 요약 데이터 (라인별)
+ /// 제조지시현황 요약 데이터
public class EWLKMfgOrderSummaryData
{
- /// 공장구분 (탭 레이블로 사용)
- public string FactoryName { get; set; } = string.Empty;
+ /// 공장구분
+ public string FactoryName { get; internal set; } = string.Empty;
/// 전체 제조 목표량
- public string TotalTarget { get; set; } = string.Empty;
+ public string TotalTarget { get; internal set; } = string.Empty;
/// 실제 실적량
- public string TotalActual { get; set; } = string.Empty;
+ public string TotalActual { get; internal set; } = string.Empty;
/// 전체 제조 진척률 (0~100)
- public float TotalProgress { get; set; }
+ public float TotalProgress { get; internal set; }
/// 일 설비가동률 가중대수% (0~100)
- public float EquipRate { get; set; }
+ public float EquipRate { get; internal set; }
/// 가동 중 설비 수
- public int ActiveCount { get; set; }
+ public int ActiveCount { get; internal set; }
/// 전체 설비 수
- public int TotalCount { get; set; }
+ public int TotalCount { get; internal set; }
}
/// 설비별 제조지시 행 데이터
public class EWLKMfgOrderRowData
{
/// 설비명
- public string EquipName { get; set; } = string.Empty;
+ public string EquipName { get; internal set; } = string.Empty;
/// 지시번호
- public string OrderNo { get; set; } = string.Empty;
+ public string OrderNo { get; internal set; } = string.Empty;
/// 품목코드
- public string ItemCode { get; set; } = string.Empty;
+ public string ItemCode { get; internal set; } = string.Empty;
/// 품목명
- public string ItemName { get; set; } = string.Empty;
+ public string ItemName { get; internal set; } = string.Empty;
/// 시작시간
- public string StartTime { get; set; } = string.Empty;
+ public string StartTime { get; internal set; } = string.Empty;
- /// 목표량
- public string Target { get; set; } = string.Empty;
+ /// 종료시간
+ public string EndTime { get; internal set; } = string.Empty;
- /// 달성률 (0~100). IsStopped가 true면 사용되지 않음
- public float Achievement { get; set; }
+ /// 총 작업 시간
+ public string TotalWorkTime { get; internal set; } = string.Empty;
- /// 계획 정지 여부 (true면 달성률 대신 "계획 정지" 표시)
- public bool IsStopped { get; set; }
+ /// 계획 생산량(kg)
+ public string PlanProduction { get; internal set; } = string.Empty;
+
+ /// 실 생산량(kg)
+ public string ActualProduction { get; internal set; } = string.Empty;
+
+ /// 달성률/진척률 (0~100)
+ public float Achievement { get; internal set; }
+
+ /// 계획 정지 여부
+ public bool IsStopped { get; internal set; }
+
+ /// 플레이스홀더 행을 생성합니다.
+ public static EWLKMfgOrderRowData CreatePlaceholder()
+ {
+ return new EWLKMfgOrderRowData
+ {
+ EquipName = "-",
+ OrderNo = "-",
+ ItemCode = "-",
+ ItemName = "-",
+ StartTime = "-",
+ EndTime = "-",
+ TotalWorkTime = "-",
+ PlanProduction = "-",
+ ActualProduction = "-",
+ Achievement = 0f,
+ IsStopped = false,
+ };
+ }
}
- /// 공장 라인별 제조지시 데이터 (요약 + 행 목록)
+ /// 제조지시현황 데이터 (요약 + 행 목록)
public class EWLKMfgOrderLineData
{
- /// 기본 라인 이름 (MQTT summary 수신 전 초기값)
- public string DefaultName { get; }
-
/// 요약 데이터
public EWLKMfgOrderSummaryData Summary { get; } = new();
- /// 설비별 행 목록 (ListView itemsSource)
+ /// 설비별 행 목록
public List Rows { get; } = new();
-
- /// MQTT 수신 전 초기 탭 레이블
- public EWLKMfgOrderLineData(string defaultName) => DefaultName = defaultName;
- }
-
- /// 제조지시현황 전체 데이터 (2개 라인)
- public class EWLKMfgOrderData
- {
- /// 라인 1
- public EWLKMfgOrderLineData Line1 { get; } = new("라인 1");
-
- /// 라인 2
- public EWLKMfgOrderLineData Line2 { get; } = new("라인 2");
}
}
diff --git a/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderMqttService.cs b/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderMqttService.cs
index 4a36bd72..1a4afa54 100644
--- a/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderMqttService.cs
+++ b/Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderMqttService.cs
@@ -8,105 +8,53 @@ namespace UVC.EnglewoodLAB.Data
{
///
/// 제조지시현황 데이터를 MQTT로 수신하는 서비스.
- /// 2개 라인의 요약 및 설비 행 데이터를 구독합니다.
- /// DataRepository.Instance.MqttReceiver(공유 수신기)를 사용합니다.
+ /// 요약 및 설비 행 데이터를 구독합니다.
///
///
- /// MQTT 토픽 구조:
- /// ewlk/mfgorder/line1/summary — 라인1 요약 (DataObject)
- /// ewlk/mfgorder/line1/rows — 라인1 설비 행 배열 (DataArray)
- /// ewlk/mfgorder/line2/summary — 라인2 요약 (DataObject)
- /// ewlk/mfgorder/line2/rows — 라인2 설비 행 배열 (DataArray)
- ///
- /// Summary JSON 스키마:
- ///
- /// {
- /// "factory_name": "2공장 제조1",
- /// "total_target": "47,910.2 Kg",
- /// "total_actual": "38,889.0 Kg",
- /// "total_progress": 34.2,
- /// "equip_rate": 0.0,
- /// "active_count": 0,
- /// "total_count": 47
- /// }
- ///
- ///
- /// Rows JSON 스키마 (배열):
- ///
- /// [
- /// {
- /// "equip_name": "UHM50",
- /// "order_no": "-",
- /// "item_code": "-",
- /// "item_name": "-",
- /// "start_time": "-",
- /// "target": "-",
- /// "achievement": 0.0,
- /// "status": "stopped"
- /// },
- /// ...
- /// ]
- ///
+ /// MQTT 토픽:
+ /// ewlk/mfgorder/line1/summary — 요약 (DataObject)
+ /// ewlk/mfgorder/line1/rows — 설비 행 배열 (DataArray)
///
public class EWLKMfgOrderMqttService : IDisposable
{
- // ── MQTT 토픽 ──────────────────────────────────────────────
- private const string TopicLine1Summary = "ewlk/mfgorder/line1/summary";
- private const string TopicLine1Rows = "ewlk/mfgorder/line1/rows";
- private const string TopicLine2Summary = "ewlk/mfgorder/line2/summary";
- private const string TopicLine2Rows = "ewlk/mfgorder/line2/rows";
+ private const string TopicSummary = "ewlk/mfgorder/line1/summary";
+ private const string TopicRows = "ewlk/mfgorder/line1/rows";
- // ── DataMask ──────────────────────────────────────────────
private static readonly DataMask s_SummaryMask = CreateSummaryMask();
private static readonly DataMask s_RowMask = CreateRowMask();
private bool _subscribed;
private bool _disposed;
- // ── 공개 API ──────────────────────────────────────────────
-
/// 가장 최근에 수신한 제조지시 데이터
- public EWLKMfgOrderData CurrentData { get; } = new();
+ public EWLKMfgOrderLineData CurrentData { get; } = new();
- /// 라인 데이터(요약 또는 행)가 갱신될 때 발생 — 메인 스레드에서 호출됩니다.
- public event Action? OnDataUpdated;
+ /// 데이터 갱신 시 발생 — 메인 스레드에서 호출됩니다.
+ public event Action? OnDataUpdated;
- // ── 구독 관리 ──────────────────────────────────────────────
-
- ///
- /// DataRepository 공유 수신기에 4개 토픽을 등록합니다.
- /// MqttReceiver.Start()는 EWLKSceneMain에서 호출합니다.
- ///
+ /// MQTT 토픽을 구독합니다.
public void Subscribe()
{
if (_subscribed || _disposed) return;
_subscribed = true;
var receiver = DataRepository.Instance.MqttReceiver;
- receiver.Add(BuildSummaryConfig(TopicLine1Summary,
- data => UpdateSummary(CurrentData.Line1, data)));
- receiver.Add(BuildRowsConfig(TopicLine1Rows,
- data => UpdateRows(CurrentData.Line1, data)));
- receiver.Add(BuildSummaryConfig(TopicLine2Summary,
- data => UpdateSummary(CurrentData.Line2, data)));
- receiver.Add(BuildRowsConfig(TopicLine2Rows,
- data => UpdateRows(CurrentData.Line2, data)));
+ receiver.Add(BuildSummaryConfig(TopicSummary, UpdateSummary));
+ receiver.Add(BuildRowsConfig(TopicRows, UpdateRows));
}
- /// DataRepository 공유 수신기에서 4개 토픽 구독을 해제합니다.
+ /// MQTT 토픽 구독을 해제합니다.
public void Unsubscribe()
{
if (!_subscribed) return;
_subscribed = false;
var receiver = DataRepository.Instance.MqttReceiver;
- receiver.Remove(TopicLine1Summary);
- receiver.Remove(TopicLine1Rows);
- receiver.Remove(TopicLine2Summary);
- receiver.Remove(TopicLine2Rows);
+ receiver.Remove(TopicSummary);
+ receiver.Remove(TopicRows);
}
- // ── 내부 구현 ──────────────────────────────────────────────
+ // ── 내부 구현 ──
private static DataMask CreateSummaryMask()
{
@@ -124,14 +72,17 @@ namespace UVC.EnglewoodLAB.Data
private static DataMask CreateRowMask()
{
var mask = new DataMask();
- mask["equip_name"] = "";
- mask["order_no"] = "";
- mask["item_code"] = "";
- mask["item_name"] = "";
- mask["start_time"] = "";
- mask["target"] = "";
- mask["achievement"] = 0.0f;
- mask["status"] = ""; // "running" | "stopped"
+ mask["equip_name"] = "";
+ mask["order_no"] = "";
+ mask["item_code"] = "";
+ mask["item_name"] = "";
+ mask["start_time"] = "";
+ mask["end_time"] = "";
+ mask["total_work_time"] = "";
+ mask["plan_production"] = "";
+ mask["actual_production"] = "";
+ mask["achievement"] = 0.0f;
+ mask["status"] = "";
return mask;
}
@@ -147,12 +98,11 @@ namespace UVC.EnglewoodLAB.Data
.SetDataMapper(new DataMapper(s_RowMask))
.SetHandler(handler);
- /// 요약 DataObject를 SummaryData에 반영합니다.
- private void UpdateSummary(EWLKMfgOrderLineData line, IDataObject? data)
+ private void UpdateSummary(IDataObject? data)
{
if (data is not DataObject obj) return;
- var s = line.Summary;
+ var s = CurrentData.Summary;
s.FactoryName = obj.GetString("factory_name") ?? string.Empty;
s.TotalTarget = obj.GetString("total_target") ?? string.Empty;
s.TotalActual = obj.GetString("total_actual") ?? string.Empty;
@@ -164,35 +114,32 @@ namespace UVC.EnglewoodLAB.Data
OnDataUpdated?.Invoke(CurrentData);
}
- ///
- /// 행 DataArray를 파싱하여 Rows를 교체합니다.
- /// DataArray는 List<DataObject>를 상속합니다.
- ///
- private void UpdateRows(EWLKMfgOrderLineData line, IDataObject? data)
+ private void UpdateRows(IDataObject? data)
{
if (data is not DataArray arr) return;
- line.Rows.Clear();
+ CurrentData.Rows.Clear();
foreach (var obj in arr)
{
- line.Rows.Add(new EWLKMfgOrderRowData
+ CurrentData.Rows.Add(new EWLKMfgOrderRowData
{
- EquipName = obj.GetString("equip_name") ?? string.Empty,
- OrderNo = obj.GetString("order_no") ?? string.Empty,
- ItemCode = obj.GetString("item_code") ?? string.Empty,
- ItemName = obj.GetString("item_name") ?? string.Empty,
- StartTime = obj.GetString("start_time") ?? string.Empty,
- Target = obj.GetString("target") ?? string.Empty,
- Achievement = obj.GetFloat("achievement") ?? 0f,
- IsStopped = (obj.GetString("status") ?? "") == "stopped",
+ EquipName = obj.GetString("equip_name") ?? string.Empty,
+ OrderNo = obj.GetString("order_no") ?? string.Empty,
+ ItemCode = obj.GetString("item_code") ?? string.Empty,
+ ItemName = obj.GetString("item_name") ?? string.Empty,
+ StartTime = obj.GetString("start_time") ?? string.Empty,
+ EndTime = obj.GetString("end_time") ?? string.Empty,
+ TotalWorkTime = obj.GetString("total_work_time") ?? string.Empty,
+ PlanProduction = obj.GetString("plan_production") ?? string.Empty,
+ ActualProduction = obj.GetString("actual_production") ?? string.Empty,
+ Achievement = obj.GetFloat("achievement") ?? 0f,
+ IsStopped = (obj.GetString("status") ?? "") == "stopped",
});
}
OnDataUpdated?.Invoke(CurrentData);
}
- // ── IDisposable ────────────────────────────────────────────
-
public void Dispose()
{
if (_disposed) return;
diff --git a/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs b/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs
index 5c9fcc11..c7fc4ed5 100644
--- a/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs
+++ b/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs
@@ -482,7 +482,7 @@ namespace UVC.EnglewoodLAB
BindMenuPopupContent(menuId, _menuPopup);
// 메뉴별 사이즈 조정 (기본: 전체 폭)
- if (menuId == "work_explorer")
+ if (menuId is "work_explorer" or "equip_list")
{
_menuPopup.style.right = StyleKeyword.Auto;
_menuPopup.style.width = new Length(40, LengthUnit.Percent);
@@ -558,6 +558,14 @@ namespace UVC.EnglewoodLAB
popup.AddContent(packContent);
break;
+ case "equip_list":
+ var equipContent = new EWLKEquipListContent();
+ // TODO: MQTT 서비스 연결 시 아래 패턴 적용
+ // equipContent.UpdateData(equipListMqtt.CurrentData);
+ // equipListMqtt.OnDataUpdated += equipContent.UpdateData;
+ popup.AddContent(equipContent);
+ break;
+
case "work_explorer":
var workExplorerContent = new EWLKWorkExplorerContent();
// TODO: MQTT 서비스 연결 시 아래 패턴 적용
diff --git a/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKMfgOrderModalContent.cs b/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKMfgOrderModalContent.cs
index 085fb8b4..d3de0be6 100644
--- a/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKMfgOrderModalContent.cs
+++ b/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKMfgOrderModalContent.cs
@@ -4,172 +4,237 @@ using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UVC.EnglewoodLAB.Data;
+using UVC.UIToolkit;
namespace UVC.EnglewoodLAB.UIToolkit
{
///
- /// 제조지시현황 모달 콘텐츠.
- /// 2개 라인 탭, 요약 바, 설비 행 ListView로 구성됩니다.
- /// MQTT 데이터가 없는 경우 플레이스홀더 행을 표시합니다.
+ /// 제조지시현황 컨텐츠.
+ /// 2개 라인 탭, 요약 바 (목표량/실적량 + 진척률/가동률), 11컬럼 테이블.
///
[UxmlElement]
public partial class EWLKMfgOrderModalContent : VisualElement, IDisposable
{
private const string UssPath = "EWLK/UIToolkit/Main/EWLKMfgOrderModalContentUss";
+ private const int ItemNameMaxChars = 5;
- // ── 탭 ──────────────────────────────────────────────
+ // ── 탭 ──
private readonly Button _tab1Btn;
private readonly Button _tab2Btn;
- // ── 요약 바 ─────────────────────────────────────────
+ // ── 요약 바 (좌측) ──
private readonly Label _totalTargetLabel;
+ private readonly VisualElement _totalTargetFill;
private readonly Label _totalActualLabel;
+ private readonly VisualElement _totalActualFill;
+
+ // ── 요약 바 (우측) ──
private readonly Label _totalProgressLabel;
private readonly VisualElement _totalProgressFill;
private readonly Label _equipRateLabel;
private readonly VisualElement _equipRateFill;
private readonly Label _countLabel;
- // ── 테이블 ───────────────────────────────────────────
- private readonly ListView _listView;
+ // ── 테이블 ──
+ private readonly ScrollView _scrollView;
+ private readonly VisualElement _rowContainer;
- // ── 상태 ─────────────────────────────────────────────
- private EWLKMfgOrderData? _data;
+ // ── 검색 ──
+ private readonly TextField _searchField;
+ private string _searchText = string.Empty;
+
+ // ── 상태 ──
private EWLKMfgOrderLineData? _currentLine;
- private int _activeTabIndex;
+ private List _allRows = new();
private List _displayRows = new();
- // ── 플레이스홀더 (MQTT 연결 전 기본 표시용) ──────────
private static readonly List s_PlaceholderRows = CreatePlaceholderRows();
- // ────────────────────────────────────────────────────
+ // 11개 컬럼 정의 (CSS 클래스, 헤더 텍스트)
+ private static readonly (string cls, string header)[] s_Columns =
+ {
+ ("col-no", "No"),
+ ("col-equip", "설비명"),
+ ("col-order", "지시 번호"),
+ ("col-item-code", "품목 코드"),
+ ("col-item-name", "품목명"),
+ ("col-start-time", "시작 시간"),
+ ("col-end-time", "종료 시간"),
+ ("col-work-time", "총 작업 시간"),
+ ("col-plan-prod", "계획 생산량(kg)"),
+ ("col-actual-prod", "실 생산량(kg)"),
+ ("col-achievement", "진척률(%)"),
+ };
public EWLKMfgOrderModalContent()
{
- styleSheets.Add(Resources.Load(UssPath));
+ var uss = Resources.Load(UssPath);
+ if (uss != null) styleSheets.Add(uss);
+
AddToClassList("ewlk-mfgorder");
- // ── 탭 바 ────────────────────────────────────────
+ // ── 탭 바 ──
var tabBar = new VisualElement();
tabBar.AddToClassList("ewlk-mfgorder__tab-bar");
- _tab1Btn = new Button(() => SelectTab(0)) { text = "라인 1" };
+ _tab1Btn = new Button(() => SelectTab(0)) { text = "2공장 제조1 목표 현황판" };
_tab1Btn.AddToClassList("ewlk-mfgorder__tab");
- _tab2Btn = new Button(() => SelectTab(1)) { text = "라인 2" };
- _tab2Btn.AddToClassList("ewlk-mfgorder__tab");
+ _tab1Btn.AddToClassList("ewlk-mfgorder__tab--active");
+ _tab2Btn = _tab1Btn; // 단일 탭 (호환성 유지)
tabBar.Add(_tab1Btn);
- tabBar.Add(_tab2Btn);
Add(tabBar);
- // ── 요약 바 ──────────────────────────────────────
+ // ── 요약 바 (좌측: 목표량+실적량, 우측: 진척률+가동률) ──
var summaryBar = new VisualElement();
summaryBar.AddToClassList("ewlk-mfgorder__summary-bar");
- summaryBar.Add(MakeStaticItem("전체 제조 목표량", out _totalTargetLabel));
- summaryBar.Add(MakeStaticItem("실제 실적량", out _totalActualLabel));
- summaryBar.Add(MakeProgressItem("전체 제조 진척률",
- out _totalProgressFill, out _totalProgressLabel));
- Add(summaryBar);
- // ── 설비가동률 행 ─────────────────────────────────
+ // 좌측 요약
+ var summaryLeft = new VisualElement();
+ summaryLeft.AddToClassList("ewlk-mfgorder__summary-left");
+ summaryLeft.Add(MakeBarItem("전체 제조 목표량", out _totalTargetLabel, out _totalTargetFill));
+ summaryLeft.Add(MakeBarItem("실제 실적량", out _totalActualLabel, out _totalActualFill));
+ summaryBar.Add(summaryLeft);
+
+ // 우측 요약
+ var summaryRight = new VisualElement();
+ summaryRight.AddToClassList("ewlk-mfgorder__summary-right");
+ summaryRight.Add(MakeBarItem("전체 제조 진척률", out _totalProgressLabel, out _totalProgressFill));
+
+ // 일 설비 가동율: [라벨] [%+대수 세로] [프로그레스 바]
var equipRow = new VisualElement();
- equipRow.AddToClassList("ewlk-mfgorder__equip-row");
+ equipRow.AddToClassList("ewlk-mfgorder__bar-item");
- var equipTitle = new Label("일 설비가동률(가중대수%)");
- equipTitle.AddToClassList("ewlk-mfgorder__equip-title");
+ var equipTitle = new Label("일 설비 가동율");
+ equipTitle.AddToClassList("ewlk-mfgorder__bar-item-title");
equipRow.Add(equipTitle);
- var equipBarWrap = new VisualElement();
- equipBarWrap.AddToClassList("ewlk-mfgorder__progress-wrap");
+ // % 와 대수를 세로로 묶기
+ var equipValueStack = new VisualElement();
+ equipValueStack.AddToClassList("ewlk-mfgorder__equip-value-stack");
+ _equipRateLabel = new Label("-");
+ _equipRateLabel.AddToClassList("ewlk-mfgorder__bar-item-value");
+ equipValueStack.Add(_equipRateLabel);
+ _countLabel = new Label("- / - 대");
+ _countLabel.AddToClassList("ewlk-mfgorder__count");
+ equipValueStack.Add(_countLabel);
+ equipRow.Add(equipValueStack);
+
+ // 프로그레스 바
+ var equipBarBg = new VisualElement();
+ equipBarBg.AddToClassList("ewlk-mfgorder__progress-wrap");
_equipRateFill = new VisualElement();
_equipRateFill.AddToClassList("ewlk-mfgorder__progress-fill");
- equipBarWrap.Add(_equipRateFill);
- equipRow.Add(equipBarWrap);
+ equipBarBg.Add(_equipRateFill);
+ equipRow.Add(equipBarBg);
- _equipRateLabel = new Label("-");
- _equipRateLabel.AddToClassList("ewlk-mfgorder__equip-rate");
- equipRow.Add(_equipRateLabel);
+ summaryRight.Add(equipRow);
- _countLabel = new Label("- / -");
- _countLabel.AddToClassList("ewlk-mfgorder__count");
- equipRow.Add(_countLabel);
- Add(equipRow);
+ summaryBar.Add(summaryRight);
+ Add(summaryBar);
- // ── 테이블 헤더 ───────────────────────────────────
+ // ── 검색창 ──
+ var searchRow = new VisualElement();
+ searchRow.AddToClassList("ewlk-mfgorder__search-row");
+
+ var searchIcon = new Label(UTKMaterialIcons.Search);
+ searchIcon.AddToClassList("ewlk-mfgorder__search-icon");
+ UTKMaterialIcons.ApplyIconStyle(searchIcon, 16);
+ searchRow.Add(searchIcon);
+
+ _searchField = new TextField();
+ _searchField.AddToClassList("ewlk-mfgorder__search-field");
+ _searchField.AddToClassList("ewlk-mfgorder__search-field--placeholder");
+ _searchField.focusable = true;
+ _searchField.pickingMode = PickingMode.Position;
+ _searchField.value = SearchPlaceholder;
+ _searchField.RegisterCallback(_ =>
+ {
+ if (_searchField.value == SearchPlaceholder)
+ {
+ _searchField.value = string.Empty;
+ _searchField.RemoveFromClassList("ewlk-mfgorder__search-field--placeholder");
+ }
+ });
+ _searchField.RegisterCallback(_ =>
+ {
+ if (string.IsNullOrEmpty(_searchField.value))
+ {
+ _searchField.value = SearchPlaceholder;
+ _searchField.AddToClassList("ewlk-mfgorder__search-field--placeholder");
+ }
+ });
+ _searchField.RegisterCallback>(OnSearchChanged);
+
+ // picking/focusable + 배경색 (TextField 내부 요소는 USS specificity로 덮어쓸 수 없음)
+ var textInput = _searchField.Q("unity-text-input");
+ if (textInput != null)
+ {
+ textInput.pickingMode = PickingMode.Position;
+ textInput.focusable = true;
+ textInput.style.backgroundColor = Color.white;
+ textInput.style.borderTopWidth = 0;
+ textInput.style.borderBottomWidth = 0;
+ textInput.style.borderLeftWidth = 0;
+ textInput.style.borderRightWidth = 0;
+ }
+
+ searchRow.pickingMode = PickingMode.Position;
+ searchRow.Add(_searchField);
+
+ Add(searchRow);
+
+ // ── 테이블 헤더 ──
Add(BuildTableHeader());
- // ── ListView ─────────────────────────────────────
- _listView = new ListView
- {
- makeItem = MakeRowElement,
- bindItem = BindRowElement,
- fixedItemHeight = 28,
- selectionType = SelectionType.None,
- };
- _listView.AddToClassList("ewlk-mfgorder__list");
- Add(_listView);
+ // ── ScrollView + 행 컨테이너 ──
+ _scrollView = new ScrollView(ScrollViewMode.Vertical);
+ _scrollView.AddToClassList("ewlk-mfgorder__list");
+ _rowContainer = new VisualElement();
+ _scrollView.Add(_rowContainer);
+ Add(_scrollView);
- SetTabActive(0);
-
- // 데이터가 없어도 플레이스홀더로 레이아웃 표시
RefreshView();
}
- // ── 공개 API ──────────────────────────────────────────
+ // ── 공개 API ──
- ///
- /// 데이터를 갱신합니다. MQTT 수신 시 호출됩니다 (메인 스레드 보장).
- ///
- public void UpdateData(EWLKMfgOrderData data)
+ /// MQTT 데이터 갱신.
+ public void UpdateData(EWLKMfgOrderLineData data)
{
- _data = data;
+ if (!string.IsNullOrEmpty(data.Summary.FactoryName))
+ _tab1Btn.text = data.Summary.FactoryName;
- // 공장구분이 수신되면 탭 레이블 갱신
- if (!string.IsNullOrEmpty(data.Line1.Summary.FactoryName))
- _tab1Btn.text = data.Line1.Summary.FactoryName;
- if (!string.IsNullOrEmpty(data.Line2.Summary.FactoryName))
- _tab2Btn.text = data.Line2.Summary.FactoryName;
-
- _currentLine = _activeTabIndex == 0 ? data.Line1 : data.Line2;
+ _currentLine = data;
RefreshView();
}
public void Dispose() { }
- // ── 탭 선택 ───────────────────────────────────────────
+ // ── 탭 (단일 탭, 호환성 유지) ──
private void SelectTab(int index)
{
- _activeTabIndex = index;
- SetTabActive(index);
- if (_data != null)
- _currentLine = index == 0 ? _data.Line1 : _data.Line2;
RefreshView();
}
- private void SetTabActive(int index)
- {
- _tab1Btn.EnableInClassList("ewlk-mfgorder__tab--active", index == 0);
- _tab2Btn.EnableInClassList("ewlk-mfgorder__tab--active", index == 1);
- }
-
- // ── 뷰 갱신 ───────────────────────────────────────────
+ // ── 뷰 갱신 ──
private void RefreshView()
{
if (_currentLine == null)
{
- // 데이터 없음 — 플레이스홀더 표시
_totalTargetLabel.text = "-";
_totalActualLabel.text = "-";
_totalProgressLabel.text = "-";
_totalProgressFill.style.width = Length.Percent(0f);
+ _totalTargetFill.style.width = Length.Percent(0f);
+ _totalActualFill.style.width = Length.Percent(0f);
_equipRateLabel.text = "-";
_equipRateFill.style.width = Length.Percent(0f);
- _countLabel.text = "- / -";
+ _countLabel.text = "- / - 대";
_displayRows = s_PlaceholderRows;
- _listView.itemsSource = _displayRows;
- _listView.Rebuild();
+ RebuildRows();
return;
}
@@ -178,184 +243,213 @@ namespace UVC.EnglewoodLAB.UIToolkit
_totalTargetLabel.text = Or(s.TotalTarget, "-");
_totalActualLabel.text = Or(s.TotalActual, "-");
_totalProgressLabel.text = $"{s.TotalProgress:F1}%";
- _totalProgressFill.style.width =
- Length.Percent(Mathf.Clamp(s.TotalProgress, 0f, 100f));
+
+ _totalTargetFill.style.width = Length.Percent(100f);
+ float actualRatio = s.TotalProgress > 0 ? Mathf.Clamp(s.TotalProgress, 0f, 100f) : 0f;
+ _totalActualFill.style.width = Length.Percent(actualRatio);
+ _totalProgressFill.style.width = Length.Percent(Mathf.Clamp(s.TotalProgress, 0f, 100f));
_equipRateLabel.text = $"{s.EquipRate:F1}%";
- _equipRateFill.style.width =
- Length.Percent(Mathf.Clamp(s.EquipRate, 0f, 100f));
- _countLabel.text = $"{s.ActiveCount} / {s.TotalCount}";
+ _equipRateFill.style.width = Length.Percent(Mathf.Clamp(s.EquipRate, 0f, 100f));
+ _countLabel.text = $"{s.ActiveCount} / {s.TotalCount} 대";
- _displayRows = _currentLine.Rows;
- _listView.itemsSource = _displayRows;
- _listView.Rebuild();
+ _allRows = _currentLine.Rows;
+ ApplyFilter();
}
- // ── ListView 바인딩 ───────────────────────────────────
+ // ── 검색 ──
+
+ private const string SearchPlaceholder = "설비명, 지시번호, 품목 코드, 품목명을 검색해 보세요.";
+
+ private void OnSearchChanged(ChangeEvent evt)
+ {
+ var val = evt.newValue ?? string.Empty;
+ _searchText = val == SearchPlaceholder ? string.Empty : val;
+ ApplyFilter();
+ }
+
+ /// 검색어로 행을 필터링하고 재생성합니다.
+ private void ApplyFilter()
+ {
+ if (string.IsNullOrWhiteSpace(_searchText))
+ {
+ _displayRows = _allRows;
+ }
+ else
+ {
+ var keyword = _searchText.Trim();
+ _displayRows = new List();
+ foreach (var row in _allRows)
+ {
+ if (Contains(row.EquipName, keyword) ||
+ Contains(row.OrderNo, keyword) ||
+ Contains(row.ItemCode, keyword) ||
+ Contains(row.ItemName, keyword))
+ {
+ _displayRows.Add(row);
+ }
+ }
+ }
+
+ RebuildRows();
+ }
+
+ private static bool Contains(string source, string keyword) =>
+ !string.IsNullOrEmpty(source) &&
+ source.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0;
+
+ /// ScrollView 내 행을 재생성합니다.
+ private void RebuildRows()
+ {
+ _rowContainer.Clear();
+ for (int i = 0; i < _displayRows.Count; i++)
+ {
+ var row = MakeRowElement();
+ BindRowElement(row, i);
+ _rowContainer.Add(row);
+ }
+ }
+
+ // ── ListView 바인딩 ──
- ///
- /// 행 요소를 생성합니다. 모든 셀과 서브 요소를 미리 생성합니다.
- ///
private static VisualElement MakeRowElement()
{
var row = new VisualElement();
row.AddToClassList("ewlk-mfgorder__row");
- // 문자열 셀 6개 (설비명~목표량)
- var textCols = new[] {
- "col-equip", "col-order", "col-item-code",
- "col-item-name", "col-time", "col-target",
- };
- foreach (var cls in textCols)
+ foreach (var (cls, _) in s_Columns)
{
var cell = new VisualElement();
cell.AddToClassList("ewlk-mfgorder__cell");
cell.AddToClassList(cls);
- var lbl = new Label();
- lbl.AddToClassList("ewlk-mfgorder__cell-label");
- cell.Add(lbl);
+
+ if (cls == "col-achievement")
+ {
+ // 진척률: 프로그레스 바 + 퍼센트 라벨
+ var barWrap = new VisualElement();
+ barWrap.name = "bar-wrap";
+ barWrap.AddToClassList("ewlk-mfgorder__progress-wrap");
+ var fill = new VisualElement();
+ fill.name = "achieve-fill";
+ fill.AddToClassList("ewlk-mfgorder__progress-fill");
+ barWrap.Add(fill);
+ cell.Add(barWrap);
+
+ var pctLbl = new Label();
+ pctLbl.name = "pct-label";
+ pctLbl.AddToClassList("ewlk-mfgorder__pct");
+ cell.Add(pctLbl);
+ }
+ else
+ {
+ var lbl = new Label();
+ lbl.AddToClassList("ewlk-mfgorder__cell-label");
+
+ // 품목명: 텍스트 줄임 + 툴팁
+ if (cls == "col-item-name")
+ lbl.AddToClassList("ewlk-mfgorder__cell-label--ellipsis");
+
+ cell.Add(lbl);
+ }
+
row.Add(cell);
}
- // 달성률 셀 (progress + stopped-label + pct-label)
- var achieveCell = new VisualElement();
- achieveCell.AddToClassList("ewlk-mfgorder__cell");
- achieveCell.AddToClassList("col-achievement");
-
- var barWrap = new VisualElement();
- barWrap.name = "bar-wrap";
- barWrap.AddToClassList("ewlk-mfgorder__progress-wrap");
- var fill = new VisualElement();
- fill.name = "achieve-fill";
- fill.AddToClassList("ewlk-mfgorder__progress-fill");
- barWrap.Add(fill);
-
- var stoppedLbl = new Label("계획 정지");
- stoppedLbl.name = "stopped-label";
- stoppedLbl.AddToClassList("ewlk-mfgorder__stopped");
-
- var pctLbl = new Label();
- pctLbl.name = "pct-label";
- pctLbl.AddToClassList("ewlk-mfgorder__pct");
-
- achieveCell.Add(barWrap);
- achieveCell.Add(stoppedLbl);
- achieveCell.Add(pctLbl);
- row.Add(achieveCell);
-
return row;
}
- ///
- /// ListView 바인딩. _displayRows(실데이터 또는 플레이스홀더)를 사용합니다.
- ///
private void BindRowElement(VisualElement element, int index)
{
if (index >= _displayRows.Count) return;
var d = _displayRows[index];
- SetLabel(element, "col-equip", d.EquipName);
- SetLabel(element, "col-order", d.OrderNo);
+ // No (1-based)
+ SetLabel(element, "col-no", (index + 1).ToString());
+ SetLabel(element, "col-equip", d.EquipName);
+ SetLabel(element, "col-order", d.OrderNo);
SetLabel(element, "col-item-code", d.ItemCode);
- SetLabel(element, "col-item-name", d.ItemName);
- SetLabel(element, "col-time", d.StartTime);
- SetLabel(element, "col-target", d.Target);
- var barWrap = element.Q("bar-wrap");
- var fill = element.Q("achieve-fill");
- var stoppedLbl = element.Q