feat(query-tool): rewrite frontend with ECharts tree, multi-select, and modular composables

Replace the monolithic useQueryToolData composable and nested Vue component tree
with a modular architecture: useLotResolve, useLotLineage, useLotDetail, and
useEquipmentQuery. Introduce ECharts TreeChart (LR orthogonal layout) for lot
lineage visualization with multi-select support, subtree expansion, zoom/pan,
and serial number normalization. Add unified LineageEngine backend with split
descendant traversal and leaf serial number queries. Archive the query-tool-rewrite
openspec change and sync delta specs to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
egg
2026-02-13 15:25:00 +08:00
parent 653900dc15
commit 5b358d71c1
56 changed files with 7458 additions and 6201 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-12

View File

@@ -0,0 +1,155 @@
## Context
The query-tool ("批次追蹤工具") is the primary batch tracing page used by production engineers to trace LOT lineage, inspect production history, and query equipment records. The current implementation is a monolithic `App.vue` (343L) + `useQueryToolData.js` (448L) with custom CSS, no component decomposition, and flat `<ul>` lineage display.
The backend is fully ready:
- `/api/query-tool/resolve` — LOT/Serial/WO resolution (max 50/50/10 inputs)
- `/api/trace/lineage` — LineageEngine genealogy (ancestors + merges, rate limit 10/60s)
- `/api/query-tool/lot-history` — production history with workcenter filter
- `/api/query-tool/lot-associations` — materials/rejects/holds/splits/jobs
- `/api/query-tool/equipment-period` — 5 query types (status_hours, lots, materials, rejects, jobs)
- `/api/query-tool/export-csv` — 11 export types
All backend endpoints remain unchanged. This is a pure frontend rewrite.
Existing design patterns from other modernized pages:
- Tailwind tokens: `brand-500`, `surface-card`, `stroke-soft`, `state-*`, `rounded-card`, `shadow-panel`
- Shared components: `SectionCard`, `FilterToolbar`, `StatusBadge`, `PaginationControl`, `TraceProgressBar`
- Shared composables: `useAutoRefresh`, `usePaginationState`, `useQueryState`, `useTraceProgress`
- Font: Noto Sans TC / Microsoft JhengHei, body 13px
## Goals / Non-Goals
**Goals:**
- Complete frontend rewrite of query-tool with Tailwind + component architecture
- Tab-based layout: LOT tracing / Equipment query as independent tabs
- Lineage decomposition tree with auto-fire + concurrency control + growth animation
- Production timeline (Gantt-style) for both LOT history and equipment activity
- Left-right master-detail for LOT tab (tree left, detail right)
- Per-sub-tab CSV export
- URL state persistence for tab, filters, and selected lot
**Non-Goals:**
- No backend API changes
- No new npm dependencies (timeline is SVG/CSS, no chart library)
- No changes to other pages (mid-section-defect uses its own TraceProgressBar integration)
- No real-time/WebSocket features
- No pagination on lineage or association tables (datasets are bounded by single-lot scope)
## Decisions
### D1: Tab-based separation of LOT and Equipment
LOT tracing and equipment query are completely unrelated workflows. Users never need both simultaneously.
**Decision**: Two top-level tabs with independent state. Tab state tracked via URL `?tab=lot|equipment`.
**Alternative considered**: Keep as single scrollable page → rejected because the page becomes too long and users lose context scrolling between sections.
### D2: Lineage tree as left navigation panel
The lineage decomposition tree serves dual purpose: it IS the lot list AND the genealogy visualization.
**Decision**: After resolve, root nodes (resolved lots) appear in the left panel. Each root can expand to show ancestors. Clicking any node (root or ancestor) selects it and loads detail in the right panel.
**Rationale**: Eliminates the need for a separate "lot list" component. The tree naturally represents the resolve results + their relationships.
**Layout**: Left panel ~300px fixed width, right panel fills remaining space. Below 1024px, stack vertically.
### D3: Auto-fire lineage with concurrency limiter
After resolve, lineage is the primary value — users want to see the genealogy immediately.
**Decision**: Auto-fire `POST /api/trace/lineage` for each resolved lot with `concurrency=3`. Results render progressively as they arrive, animating tree growth.
**Concurrency calculation**: Rate limit is 10/60s. With concurrency=3 and avg ~1.5s per call, we'll sustain ~2 calls/s, comfortably below the limit. For 50 lots, all lineage completes in ~25s with continuous tree growth.
**Alternative considered**: On-demand only (click to expand) → rejected because the user explicitly wants to see the full picture immediately. The auto-fire + animation creates an engaging "tree growing" experience.
**Cache**: Per-lot lineage cached in a reactive Map. Cache cleared on new resolve.
### D4: Progressive tree growth animation
**Decision**: Use Vue `<TransitionGroup>` with CSS transforms:
- Root nodes: `opacity 0→1` (fade-in on resolve)
- Branch nodes: `translateX(-16px)→0` + `opacity 0→1` (slide-in from left)
- Sibling stagger: 30-50ms delay between consecutive siblings
- Level 2+ nodes animate when their parent's lineage data arrives (same animation)
**Implementation**: `LineageNode.vue` is a recursive component. Each node wraps its children in `<TransitionGroup>`. When the reactive lineage cache updates for a lot, Vue reactivity triggers child insertion, which triggers the enter transition.
### D5: TimelineChart as shared SVG component
Both LOT production timeline and equipment timeline need the same Gantt-style visualization.
**Decision**: Create a `TimelineChart.vue` component that accepts:
```
Props:
tracks: Array<{ id, label, layers: Array<{ bars: Array<{start, end, type}> }> }>
events: Array<{ trackId, time, type, label, detail }>
timeRange: { start: Date, end: Date }
colorMap: Record<string, string>
```
**Rendering**: Pure SVG with:
- Sticky left labels (CSS `position: sticky`)
- Horizontal time axis with auto-scaled ticks (hours or days)
- Multi-layer bars per track (background layer + foreground layer)
- Event markers as SVG icons (diamond shape) with hover tooltips
- Horizontal scroll container for wide timelines
**No external deps**: SVG + CSS only. Time calculations use native Date. No D3, no ECharts.
### D6: LOT Timeline data mapping
LOT production history records have TRACKINTIMESTAMP and TRACKOUTTIMESTAMP per station.
**Decision**: Map lot-history rows to TimelineChart tracks:
- Each unique WORKCENTERNAME = one track
- Each row = one bar (track-in to track-out, colored by workcenter group)
- Hold events from lot-holds = event markers
- Workcenter filter shows/hides tracks
### D7: Equipment Timeline multi-source composition
Equipment timeline overlays three data sources on a single track per equipment.
**Decision**: Fetch `status_hours`, `lots`, and `jobs` data, then compose:
- Layer 0 (background): Status bars (PRD=green, SBY=amber, UDT=red, SDT=blue-gray)
- Layer 1 (foreground): Lot processing bars (track-in to track-out)
- Events: Maintenance job markers (JOBID + CAUSECODENAME as label)
**Note**: This requires 3 API calls per equipment tab query. They can fire in parallel since they're independent.
### D8: Composable architecture
Split the monolithic `useQueryToolData.js` (448L) into focused composables:
| Composable | Responsibility | State |
|-----------|---------------|-------|
| `useLotResolve` | Input parsing, resolve API, URL state for resolve params | `resolvedLots`, `notFound`, `loading.resolving` |
| `useLotLineage` | Lineage auto-fire, concurrency limiter, tree expand/collapse state, cache | `lineageMap`, `expandedNodes`, `loadingSet` |
| `useLotDetail` | Per-lot history + associations, sub-tab active state, sub-tab cache | `activeSubTab`, `historyRows`, `associationRows`, caches |
| `useEquipmentQuery` | Equipment list, date range, 4 query types, sub-tab state | `equipment.*`, `activeSubTab`, query results |
Each composable manages its own loading/error state. No global error banner — errors display contextually within each section.
### D9: Delete legacy files
- `frontend/src/query-tool/main.js` → rewrite to minimal Vite entry (3-5 lines: import App, createApp, mount)
- `frontend/src/query-tool/style.css` → delete entirely, all styling via Tailwind
## Risks / Trade-offs
**[100+ lots from work order → tree overwhelm]** → The lineage tree with 100+ root nodes could be visually overwhelming. Mitigation: Virtual scroll or "show first 20, load more" pattern for the tree panel if needed. Start without it and evaluate.
**[Lineage auto-fire rate limit pressure]** → With 50 lots and concurrency=3, we'll make ~50 requests within ~25s. Rate limit is 10/60s which means we'd hit the limit at lot #10. Mitigation: The concurrency limiter must respect 429 responses and back off. If rate limited, pause and retry after `Retry-After` header. Alternatively, batch multiple container_ids per lineage call (backend already supports arrays).
**[Timeline SVG performance with large datasets]** → Equipment timeline spanning 90 days with 20 equipment could generate thousands of SVG elements. Mitigation: Aggregate status bars at coarse granularity for wide ranges, detailed view for narrow ranges. Start with naive rendering and optimize if needed.
**[Left panel width on narrow screens]** → 300px fixed width may be too wide on 1024-1280px screens. Mitigation: Make the left panel collapsible/resizable, or use a narrower default (240px) with a expand-on-hover pattern.
## Resolved Questions
- **Q1 (Resolved)**: Lineage API calls SHALL be per-lot independent (not batched). This preserves the progressive tree growth animation — each API response triggers a visual branch expansion. With concurrency=3 and 429 backoff, this stays within rate limits while providing engaging UX.
- **Q2 (Resolved)**: Equipment timeline SHALL use the `DW_MES_RESOURCESTATUS_SHIFT` aggregate table (8h shift granularity). This avoids querying raw status change events which are voluminous and not readily available via existing API. The 8h blocks are sufficient for timeline overview; users needing finer granularity can inspect the lots/jobs sub-tabs for exact timestamps.

View File

@@ -0,0 +1,33 @@
## Why
The query-tool page ("批次追蹤工具") is the last major page still running as a monolithic Vue SFC with custom CSS. Its 343-line App.vue and 448-line useQueryToolData composable pack all three unrelated features (LOT tracing, LOT detail, equipment period query) into one vertical scroll with no component decomposition. Lineage visualization is a flat `<ul><li>` list, production history is a static table with no timeline view, and there is no progressive loading animation despite the staged trace API being available. The page needs a complete frontend rewrite to match the Tailwind + component-based architecture used by all other modernized pages, while significantly improving the tracing UX.
## What Changes
- **Complete rewrite** of `frontend/src/query-tool/` — new component tree, composables, and Tailwind-only styling (no style.css)
- **Tab-based layout**: Split LOT tracing and equipment query into independent top-level tabs
- **Lineage decomposition tree**: Replace flat ancestor list with an interactive tree that "grows" progressively as lineage API calls return (limited concurrency, animated node insertion)
- **Left-right master-detail layout**: Lineage tree as left navigation panel, LOT detail (sub-tabs) on right
- **Production timeline** (shared `TimelineChart.vue`): Gantt-style visualization for both LOT production history (stations over time) and equipment activity (lots + maintenance + status)
- **Equipment tab redesign**: Replace 5 generic query types with 4 focused sub-tabs — production lots, maintenance records (with cause/repair/symptom codes), scrap records, and equipment timeline
- **Auto-fire lineage with concurrency control**: After resolve, lineage API calls fire automatically with concurrency=3, tree grows as results arrive
- **Per-sub-tab CSV export**: Each detail sub-tab has its own export button instead of one shared context-aware export
- **Delete legacy `main.js`**: The 448-line vanilla JS module in query-tool is dead code superseded by the Vue SFC
## Capabilities
### New Capabilities
- `query-tool-lot-trace`: LOT tracing tab — query bar, lineage decomposition tree with progressive growth animation, left-right master-detail layout, LOT detail sub-tabs (history with workcenter filter + timeline, materials, rejects, holds, splits, jobs), per-tab CSV export
- `query-tool-equipment`: Equipment query tab — equipment/date selection, 4 sub-tabs (production lots, maintenance records, scrap records, equipment timeline), per-tab CSV export
- `timeline-chart`: Shared Gantt-style timeline visualization component — horizontal time axis, configurable tracks with colored bars, event markers, tooltips, used by both LOT and equipment views
### Modified Capabilities
- `progressive-trace-ux`: Lineage tree now auto-fires with concurrency-limited parallel requests and animated progressive rendering (expanding the on-demand spec to support auto-fire mode)
## Impact
- **Frontend**: Complete rewrite of `frontend/src/query-tool/` (App.vue, composables, new component tree of ~15 files)
- **Backend**: Zero changes — all existing `/api/query-tool/*` and `/api/trace/*` endpoints remain unchanged
- **Shared UI**: New `TimelineChart.vue` component may live in `shared-ui/` or query-tool local components
- **Dead code**: `frontend/src/query-tool/main.js` (448L) and `frontend/src/query-tool/style.css` deleted
- **Dependencies**: No new npm packages — timeline rendered with SVG/CSS, tree with recursive Vue components + TransitionGroup

View File

@@ -0,0 +1,16 @@
## MODIFIED Requirements
### Requirement: query-tool lineage tab SHALL load on-demand
The query-tool lineage tree SHALL auto-fire lineage API calls after lot resolution with concurrency-limited parallel requests and progressive rendering, while preserving on-demand expand/collapse for tree navigation.
#### Scenario: Auto-fire lineage after resolve
- **WHEN** lot resolution completes with N resolved lots
- **THEN** lineage SHALL be fetched via `POST /api/trace/lineage` for each lot automatically
- **THEN** concurrent requests SHALL be limited to 3 at a time to respect rate limits (10/60s)
- **THEN** response time SHALL be ≤3s per individual lot
#### Scenario: Multiple lots lineage results cached
- **WHEN** lineage data has been fetched for multiple lots
- **THEN** each lot's lineage data SHALL be preserved independently (not re-fetched)
- **WHEN** a new resolve query is executed
- **THEN** all cached lineage data SHALL be cleared

View File

@@ -0,0 +1,72 @@
## ADDED Requirements
### Requirement: Equipment tab SHALL provide equipment selection with date range filtering
The equipment tab SHALL allow selecting multiple equipment and a date range for all sub-tab queries.
#### Scenario: Equipment and date selection
- **WHEN** the user opens the equipment tab
- **THEN** a MultiSelect dropdown SHALL list available equipment from `GET /api/query-tool/equipment-list`
- **THEN** date inputs SHALL default to the last 30 days
- **THEN** a query button SHALL trigger data loading for the active sub-tab
#### Scenario: Shared filter state across sub-tabs
- **WHEN** the user selects equipment and date range then switches sub-tabs
- **THEN** the filter values SHALL persist across all equipment sub-tabs
- **THEN** switching to a new sub-tab with the same filters SHALL trigger a fresh query for that sub-tab's data type
### Requirement: Equipment Production Lots sub-tab SHALL display lots processed by selected equipment
The Production Lots sub-tab SHALL show all lots processed on the selected equipment within the date range.
#### Scenario: Production lots query
- **WHEN** the user queries with selected equipment and date range
- **THEN** the system SHALL call `POST /api/query-tool/equipment-period` with `query_type: "lots"`
- **THEN** results SHALL display in a table showing CONTAINERID, SPECNAME, TRACK_IN, TRACK_OUT, QTY, EQUIPMENTNAME
#### Scenario: Partial track-out handling
- **WHEN** a lot has multiple track-out records (partial processing)
- **THEN** all records SHALL be displayed (not deduplicated)
### Requirement: Equipment Maintenance sub-tab SHALL display maintenance job records with expandable detail
The Maintenance sub-tab SHALL show maintenance jobs from `DW_MES_JOB` with cause/repair/symptom codes.
#### Scenario: Maintenance job list
- **WHEN** the user queries maintenance records
- **THEN** the system SHALL call `POST /api/query-tool/equipment-period` with `query_type: "jobs"`
- **THEN** results SHALL display: JOBID, STATUS, CAUSECODENAME, REPAIRCODENAME, SYMPTOMCODENAME, CREATE/COMPLETE dates
#### Scenario: Job detail expansion
- **WHEN** the user clicks on a maintenance job row
- **THEN** the row SHALL expand to show full detail including employee names, secondary codes, and related lot IDs (CONTAINERNAMES)
### Requirement: Equipment Scrap sub-tab SHALL display reject/defect records
The Scrap sub-tab SHALL show reject statistics grouped by loss reason for the selected equipment and date range.
#### Scenario: Scrap records query
- **WHEN** the user queries scrap records
- **THEN** the system SHALL call `POST /api/query-tool/equipment-period` with `query_type: "rejects"`
- **THEN** results SHALL display: EQUIPMENTNAME, LOSSREASONNAME, TOTAL_REJECT_QTY, TOTAL_DEFECT_QTY, AFFECTED_LOT_COUNT
### Requirement: Equipment Timeline sub-tab SHALL visualize equipment activity over time
The Timeline sub-tab SHALL render a Gantt-style timeline showing equipment status, lots processed, and maintenance events.
#### Scenario: Multi-layer timeline rendering
- **WHEN** the user views the equipment timeline
- **THEN** the timeline SHALL overlay three data layers: status bars (PRD/SBY/UDT/SDT), lot processing bars, and maintenance event markers
- **THEN** each equipment SHALL appear as a separate track row
#### Scenario: Status color coding
- **WHEN** the timeline renders status bars
- **THEN** PRD SHALL be green, SBY SHALL be amber, UDT SHALL be red, SDT SHALL be blue-gray
- **THEN** a legend SHALL be displayed showing the color mapping
#### Scenario: Maintenance marker interaction
- **WHEN** the user hovers over or clicks a maintenance event marker on the timeline
- **THEN** a tooltip or expanded panel SHALL show the job detail (CAUSECODENAME, REPAIRCODENAME, SYMPTOMCODENAME)
### Requirement: Each equipment sub-tab SHALL support CSV export
Every equipment sub-tab SHALL have its own export button calling the existing export API.
#### Scenario: Equipment CSV export
- **WHEN** the user clicks export on any equipment sub-tab
- **THEN** the system SHALL call `POST /api/query-tool/export-csv` with the appropriate `export_type` (equipment_lots, equipment_jobs, equipment_rejects)
- **THEN** the exported params SHALL include the current equipment_ids/equipment_names and date range

View File

@@ -0,0 +1,152 @@
## ADDED Requirements
### Requirement: Query-tool page SHALL use tab-based layout separating LOT tracing from equipment queries
The query-tool page SHALL present two top-level tabs: "LOT 追蹤" and "設備查詢", each with independent state and UI.
#### Scenario: Tab switching preserves state
- **WHEN** the user switches from LOT tab to Equipment tab and back
- **THEN** the LOT tab SHALL retain its resolved lots, lineage tree state, and selected lot detail
- **THEN** the Equipment tab SHALL retain its query results independently
#### Scenario: URL state reflects active tab
- **WHEN** the user is on a specific tab
- **THEN** the URL SHALL include a `tab` parameter (e.g., `?tab=lot` or `?tab=equipment`)
- **THEN** reloading the page SHALL restore the active tab
### Requirement: QueryBar SHALL resolve LOT/Serial/WorkOrder inputs
The query bar SHALL accept multi-value input (newline or comma-separated) with input type selection and resolve via `POST /api/query-tool/resolve`.
#### Scenario: Successful LOT resolution
- **WHEN** the user enters lot IDs and clicks resolve
- **THEN** the system SHALL call `POST /api/query-tool/resolve` with `input_type` and `values`
- **THEN** resolved lots SHALL appear as root nodes in the lineage tree
- **THEN** not-found values SHALL be displayed as warnings below the tree
#### Scenario: Work order expansion
- **WHEN** the user enters work order IDs (max 10)
- **THEN** each work order MAY expand to 100+ lots
- **THEN** all expanded lots SHALL appear as root nodes in the lineage tree
#### Scenario: Input validation
- **WHEN** the user submits empty input or exceeds limits (50 lot IDs, 50 serial numbers, 10 work orders)
- **THEN** the system SHALL display a validation error without making an API call
### Requirement: LineageTree SHALL display as a decomposition tree with progressive growth animation
After resolve completes, the lineage tree SHALL auto-fire lineage API calls for each resolved lot with concurrency control, rendering an animated tree that grows as results arrive.
#### Scenario: Auto-fire lineage after resolve
- **WHEN** lot resolution completes with N resolved lots
- **THEN** the system SHALL automatically call `POST /api/trace/lineage` for each lot
- **THEN** concurrent lineage requests SHALL be limited to 3 at a time
- **THEN** the lineage tree SHALL render root nodes immediately (resolved lots)
#### Scenario: Progressive tree growth animation
- **WHEN** a lineage API response returns for a lot
- **THEN** ancestor nodes SHALL animate into the tree (slide-in + fade, staggered 30-50ms per sibling)
- **THEN** the animation SHALL give the visual impression of a tree "growing" its branches
#### Scenario: Tree node expand/collapse
- **WHEN** the user clicks a tree node with children (ancestors)
- **THEN** children SHALL toggle between expanded and collapsed state
- **THEN** expand/collapse SHALL be a client-side operation (no additional API call)
#### Scenario: Expand-all and collapse-all
- **WHEN** the user clicks "全部展開"
- **THEN** all tree nodes at all levels SHALL expand with staggered animation
- **WHEN** the user clicks "收合"
- **THEN** all tree nodes SHALL collapse to show only root nodes
#### Scenario: Merge relationships visually distinct
- **WHEN** the lineage data includes merge relationships
- **THEN** merge nodes SHALL display a distinct icon (🔀) and/or color to differentiate from direct ancestor relationships
#### Scenario: Leaf nodes without expand affordance
- **WHEN** a tree node has no ancestors (leaf/terminal node)
- **THEN** it SHALL NOT display an expand button or clickable expand area
#### Scenario: Lineage cache prevents duplicate fetches
- **WHEN** lineage data has already been fetched for a lot
- **THEN** subsequent interactions SHALL use cached data without re-fetching
- **WHEN** a new resolve query is executed
- **THEN** the lineage cache SHALL be cleared
### Requirement: Left-right master-detail layout SHALL show tree and LOT detail side by side
The LOT tracing tab SHALL use a left-right split layout with the lineage tree on the left and LOT detail on the right.
#### Scenario: LOT selection from tree
- **WHEN** the user clicks any node in the lineage tree (root lot or ancestor)
- **THEN** the right panel SHALL load detail for that node's container ID
- **THEN** the selected node SHALL be visually highlighted in the tree
#### Scenario: Right panel sub-tabs
- **WHEN** a LOT is selected
- **THEN** the right panel SHALL display sub-tabs: 歷程 (History), 物料 (Materials), 退貨 (Rejects), Hold, Split, Job
- **THEN** each sub-tab SHALL load data on-demand when activated (not pre-fetched)
#### Scenario: Responsive behavior
- **WHEN** the viewport width is below 1024px
- **THEN** the layout SHALL stack vertically (tree above, detail below)
### Requirement: LOT History sub-tab SHALL display production history with workcenter filter
The History sub-tab SHALL show production history data from `GET /api/query-tool/lot-history` with workcenter group filtering.
#### Scenario: History table display
- **WHEN** the user selects the History sub-tab for a LOT
- **THEN** the system SHALL call `GET /api/query-tool/lot-history?container_id=X`
- **THEN** results SHALL display in a table with sticky headers and horizontal scroll
#### Scenario: Workcenter group filter
- **WHEN** the user selects workcenter groups from the filter dropdown
- **THEN** the history query SHALL include the selected groups as filter parameters
- **THEN** the history table SHALL refresh with filtered results
### Requirement: LOT Production Timeline SHALL visualize station progression over time
The History sub-tab SHALL include a timeline visualization showing the lot's journey through production stations.
#### Scenario: Timeline rendering
- **WHEN** lot history data is loaded
- **THEN** a horizontal Gantt-style timeline SHALL render with time on the X-axis
- **THEN** each workcenter/station SHALL appear as a track with a colored bar from track-in to track-out time
#### Scenario: Workcenter filter affects timeline
- **WHEN** the user filters by workcenter groups
- **THEN** the timeline SHALL show only stations matching the selected groups
- **THEN** filtered-out stations SHALL be hidden (not grayed out)
#### Scenario: Timeline event markers
- **WHEN** hold events or material consumption events exist within the timeline range
- **THEN** they SHALL be displayed as markers on the timeline with tooltip details on hover
### Requirement: LOT Association sub-tabs SHALL load data on-demand
Each association sub-tab (Materials, Rejects, Holds, Splits, Jobs) SHALL fetch data independently when activated.
#### Scenario: Association data loading
- **WHEN** the user activates a sub-tab (e.g., Materials)
- **THEN** the system SHALL call `GET /api/query-tool/lot-associations?container_id=X&type=materials`
- **THEN** results SHALL display in a table with dynamic columns
#### Scenario: Sub-tab data caching within session
- **WHEN** the user switches between sub-tabs for the same LOT
- **THEN** previously loaded sub-tab data SHALL be preserved (not re-fetched)
- **WHEN** the user selects a different LOT
- **THEN** all sub-tab caches SHALL be cleared
### Requirement: Each sub-tab SHALL support independent CSV export
Every detail sub-tab SHALL have its own export button.
#### Scenario: Per-tab export
- **WHEN** the user clicks export on the Materials sub-tab
- **THEN** the system SHALL call `POST /api/query-tool/export-csv` with `export_type: "lot_materials"` and the current container_id
- **THEN** a CSV file SHALL download with the appropriate filename
#### Scenario: Export disabled when no data
- **WHEN** a sub-tab has no data loaded or the data is empty
- **THEN** the export button SHALL be disabled
### Requirement: Legacy dead code SHALL be removed
The legacy `frontend/src/query-tool/main.js` (448L vanilla JS) and `frontend/src/query-tool/style.css` SHALL be deleted.
#### Scenario: Dead code removal
- **WHEN** the rewrite is complete
- **THEN** `frontend/src/query-tool/main.js` SHALL contain only the Vite entry point (createApp + mount)
- **THEN** `frontend/src/query-tool/style.css` SHALL be deleted (all styling via Tailwind)

View File

@@ -0,0 +1,50 @@
## ADDED Requirements
### Requirement: TimelineChart component SHALL render configurable Gantt-style timelines
A shared `TimelineChart` component SHALL accept structured track/event data and render a horizontal timeline with SVG/CSS.
#### Scenario: Basic track rendering
- **WHEN** TimelineChart receives tracks with bars (each bar having start time, end time, type)
- **THEN** it SHALL render a horizontal time axis and one row per track
- **THEN** each bar SHALL be positioned proportionally along the time axis with width reflecting duration
#### Scenario: Color mapping
- **WHEN** bars have different `type` values
- **THEN** each type SHALL be rendered with a color from the provided `colorMap` prop
- **THEN** a legend SHALL be displayed showing type-to-color mapping
#### Scenario: Event markers
- **WHEN** the component receives events (point-in-time markers)
- **THEN** each event SHALL render as a marker (diamond/triangle icon) at the corresponding time position on its track
- **THEN** hovering over a marker SHALL display a tooltip with the event label and details
#### Scenario: Time axis adapts to data range
- **WHEN** the timeline data spans hours
- **THEN** the time axis SHALL show hour ticks (e.g., 06:00, 07:00, ...)
- **WHEN** the timeline data spans days
- **THEN** the time axis SHALL show date ticks (e.g., 02-10, 02-11, ...)
#### Scenario: Horizontal scroll for long timelines
- **WHEN** the timeline data exceeds the visible width
- **THEN** the component SHALL support horizontal scrolling
- **THEN** track labels on the left SHALL remain fixed (sticky) during scroll
#### Scenario: Bar tooltip on hover
- **WHEN** the user hovers over a bar segment
- **THEN** a tooltip SHALL display the bar's label, start time, end time, and duration
### Requirement: TimelineChart SHALL support multi-layer overlapping tracks
The component SHALL support rendering multiple bar layers on a single track row (e.g., status bars behind lot bars).
#### Scenario: Overlapping layers
- **WHEN** a track has multiple layers (e.g., `statusBars` and `lotBars`)
- **THEN** background layer bars SHALL render behind foreground layer bars
- **THEN** both layers SHALL be visible (foreground bars shorter in height or semi-transparent)
### Requirement: TimelineChart SHALL have no external charting dependencies
The component SHALL be implemented using only SVG elements and CSS, with no external charting library.
#### Scenario: Zero additional dependencies
- **WHEN** the TimelineChart component is used
- **THEN** it SHALL NOT require any npm package not already in the project
- **THEN** rendering SHALL use inline SVG elements within the Vue template

View File

@@ -0,0 +1,67 @@
## 1. App Shell + Tab Layout
- [x] 1.1 Rewrite `App.vue` as tab shell with "LOT 追蹤" and "設備查詢" top-level tabs using Tailwind (gradient header, tab buttons, active indicator)
- [x] 1.2 Implement URL `?tab=lot|equipment` sync — persist and restore active tab on page load
- [x] 1.3 Rewrite `main.js` to minimal Vite entry point (createApp + mount, ~5 lines)
- [x] 1.4 Delete `style.css` — all styling via Tailwind from this point forward
## 2. QueryBar + Resolve
- [x] 2.1 Create `QueryBar.vue` — input type selector (lot_id/serial_number/work_order), multi-line textarea, resolve button, Tailwind styling consistent with other pages' FilterToolbar patterns
- [x] 2.2 Create `useLotResolve.js` composable — input parsing (split by newline/comma), validation (empty check, max limits), `POST /api/query-tool/resolve` call, reactive state (`resolvedLots`, `notFound`, `loading.resolving`, `errorMessage`)
- [x] 2.3 Wire QueryBar into `LotTraceView.vue` — resolve results feed into the lineage tree below
## 3. Lineage Tree with Progressive Growth
- [x] 3.1 Create `useLotLineage.js` composable — auto-fire lineage calls after resolve, concurrency limiter (max 3 concurrent), per-lot reactive cache (`lineageMap: Map<containerId, {ancestors, merges, loading, error}>`), expand/collapse state (`expandedNodes: Set`), cache clearing on new resolve, 429 backoff handling
- [x] 3.2 Create `LineageNode.vue` recursive component — displays node label (container name, lot type icon for merge 🔀), expand/collapse toggle for non-leaf nodes, emits `select` event on click, highlight for selected node
- [x] 3.3 Create `LineageTree.vue` — renders resolved lots as root nodes, wraps children in `<TransitionGroup>` for growth animation, "全部展開" / "收合" buttons, not-found warnings below tree
- [x] 3.4 Implement CSS transitions for tree growth — `translateX(-16px)→0` + `opacity 0→1` enter transition, 30-50ms stagger via `transition-delay` on siblings, root nodes fade-in on resolve
## 4. Left-Right Master-Detail Layout
- [x] 4.1 Create `LotTraceView.vue` — left-right split layout (left panel ~280-300px for lineage tree, right panel fills remaining), responsive stacking below 1024px
- [x] 4.2 Create `LotDetail.vue` — right panel container with sub-tab bar (歷程, 物料, 退貨, Hold, Split, Job), active tab indicator, on-demand data loading when tab activated, contextual error display
- [x] 4.3 Wire tree node selection → right panel detail loading — clicking any node in LineageTree sets `selectedContainerId` and triggers active sub-tab data fetch
## 5. LOT Detail Sub-tabs
- [x] 5.1 Create `useLotDetail.js` composable — manages `activeSubTab`, per-tab data cache (invalidated on lot change), `GET /api/query-tool/lot-history` with workcenter group filter, `GET /api/query-tool/lot-associations?type=X` for each association type, loading/error state per sub-tab
- [x] 5.2 Create `LotHistoryTable.vue` — production history table with sticky headers, workcenter group MultiSelect filter, dynamic columns, horizontal scroll
- [x] 5.3 Create `LotAssociationTable.vue` — shared table component for materials/rejects/holds/splits/jobs, dynamic columns from response, empty state message
- [x] 5.4 Add per-sub-tab `ExportButton.vue` — calls `POST /api/query-tool/export-csv` with appropriate `export_type` and `container_id`, disabled when no data, download blob as CSV
## 6. TimelineChart Shared Component
- [x] 6.1 Create `TimelineChart.vue` — props interface (`tracks`, `events`, `timeRange`, `colorMap`), SVG rendering with horizontal time axis, auto-scaled ticks (hour/day granularity), sticky left track labels
- [x] 6.2 Implement multi-layer bar rendering — background layer behind foreground layer per track, proportional positioning from time range, color from colorMap
- [x] 6.3 Implement event markers — diamond/triangle SVG icons at time positions, hover tooltip with event label and detail
- [x] 6.4 Implement horizontal scroll container — overflow-x scroll wrapper, sticky label column, responsive width
## 7. LOT Production Timeline
- [x] 7.1 Create `LotTimeline.vue` — maps lot-history rows to TimelineChart tracks (one track per WORKCENTERNAME, bars from TRACKINTIMESTAMP to TRACKOUTTIMESTAMP), respects workcenter group filter
- [x] 7.2 Overlay hold/material event markers on timeline — fetches hold events and material consumption events, renders as markers on corresponding time positions
- [x] 7.3 Integrate into History sub-tab — timeline renders above or alongside the history table, shares the workcenter filter state
## 8. Equipment Tab
- [x] 8.1 Create `EquipmentView.vue` — filter bar (MultiSelect for equipment, date range inputs, query button), sub-tab bar (生產紀錄, 維修紀錄, 報廢紀錄, Timeline), shared filter state across sub-tabs
- [x] 8.2 Create `useEquipmentQuery.js` composable — equipment list bootstrap from `GET /api/query-tool/equipment-list`, date range default (last 30 days), `POST /api/query-tool/equipment-period` calls per query type, loading/error per sub-tab, URL state sync for equipment_ids + dates
- [x] 8.3 Create `EquipmentLotsTable.vue` — production lots table (CONTAINERID, SPECNAME, TRACK_IN/OUT, QTY, EQUIPMENTNAME), sticky headers, export button
- [x] 8.4 Create `EquipmentJobsPanel.vue` — maintenance job table (JOBID, STATUS, CAUSECODENAME, REPAIRCODENAME, SYMPTOMCODENAME, dates), expandable row detail (employee names, secondary codes, CONTAINERNAMES), export button
- [x] 8.5 Create `EquipmentRejectsTable.vue` — scrap records table (EQUIPMENTNAME, LOSSREASONNAME, TOTAL_REJECT_QTY, TOTAL_DEFECT_QTY, AFFECTED_LOT_COUNT), export button
## 9. Equipment Timeline
- [x] 9.1 Create `EquipmentTimeline.vue` — composes 3 data sources (status_hours + lots + jobs) into TimelineChart tracks, fires 3 API calls in parallel, one track per equipment
- [x] 9.2 Map status data to background layer bars — PRD=green, SBY=amber, UDT=red, SDT=blue-gray
- [x] 9.3 Map lot data to foreground layer bars — lot processing bars from track-in to track-out
- [x] 9.4 Map maintenance jobs to event markers — JOBID + CAUSECODENAME as label, tooltip with full detail
## 10. Polish + Cleanup
- [x] 10.1 Full URL state sync — all filter values (tab, input_type, container_id, workcenter_groups, equipment_ids, dates, sub-tabs) persisted to URL and restored on page load via `useQueryState` or custom sync
- [x] 10.2 Responsive layout testing — verify left-right split stacks correctly below 1024px, tab layout works on mobile, timeline horizontal scroll works on touch
- [x] 10.3 Delete dead code — remove old monolithic `useQueryToolData.js`, verify no imports reference deleted files
- [x] 10.4 Visual consistency audit — verify gradient header, button styles, table styles, card borders match other modernized pages (wip-detail, hold-detail, mid-section-defect)