feat: polish reject history UI and enhance WIP filter interactions
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-22
|
||||
@@ -0,0 +1,79 @@
|
||||
## Context
|
||||
|
||||
`WIP 即時概況` 現在使用 4 個文字輸入框(WORKORDER/LOT ID/PACKAGE/TYPE)搭配 `/api/wip/meta/search` 即時建議。此模式在多條件操作時需要頻繁輸入,且首次進頁不會先拿到完整候選值。需求要改成可模糊搜尋的下拉清單,並新增 `FIRSTNAME`、`WAFERDESC` 兩個篩選維度,且篩選選項來源以快取為主。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
|
||||
- 將 WIP 概況篩選改成下拉可搜尋(參考設備即時概況「機台」篩選互動)
|
||||
- 新增 `Wafer LOT(FIRSTNAME)`、`Wafer Type(WAFERDESC)` 篩選
|
||||
- 所有篩選候選值可由快取一次取得,並支援首次載入預先填充
|
||||
- 既有 summary/matrix/hold 查詢都能吃到新舊篩選條件
|
||||
|
||||
**Non-Goals**
|
||||
|
||||
- 不改 WIP Detail 頁面的篩選 UI(仍維持現有 autocomplete)
|
||||
- 不移除既有 `/api/wip/meta/search`(保留向下相容)
|
||||
- 不變更 WIP 指標計算邏輯(僅改篩選方式與欄位)
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: 新增 `GET /api/wip/meta/filter-options` 做「一次取齊」篩選選項
|
||||
|
||||
API 回傳:
|
||||
- `workorders`
|
||||
- `lotids`
|
||||
- `packages`
|
||||
- `types`
|
||||
- `firstnames`
|
||||
- `waferdescs`
|
||||
|
||||
資料來源優先順序:
|
||||
1. WIP 快取衍生搜尋索引(`_get_wip_search_index`)
|
||||
2. WIP 快取快照(必要時)
|
||||
3. Oracle fallback(僅快取不可用時)
|
||||
|
||||
此設計讓前端在第一次查詢前就能載入完整下拉選項。
|
||||
|
||||
### D2: WIP 概況前端篩選改採 `MultiSelect`(可搜尋)
|
||||
|
||||
`frontend/src/wip-overview/components/FilterPanel.vue` 改為使用 `resource-shared/components/MultiSelect.vue`:
|
||||
- 6 個篩選欄位皆為可搜尋下拉
|
||||
- 支援多選(內部值為陣列)
|
||||
- 顯示 active chips,移除 chip 會觸發重查
|
||||
|
||||
### D3: 篩選參數以 CSV 傳遞,服務層統一解析
|
||||
|
||||
API query 維持既有參數名稱(`workorder`, `lotid`, `package`, `type`)並新增:
|
||||
- `firstname`
|
||||
- `waferdesc`
|
||||
|
||||
多選由前端以逗號串接傳遞。服務層新增 CSV 解析 helper,將單值/多值統一轉成條件。
|
||||
|
||||
### D4: 搜尋索引與快照索引擴充 Wafer 欄位
|
||||
|
||||
`wip_service` 的衍生索引加入:
|
||||
- `FIRSTNAME`
|
||||
- `WAFERDESC`
|
||||
|
||||
確保:
|
||||
- `meta/filter-options` 可直接由索引取值
|
||||
- summary/matrix/hold 可在快取路徑下高效套用 Wafer 篩選
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **參數長度風險**:多選過多時 URL 長度增加;目前以一般 dashboard 操作量可接受。
|
||||
- **跨頁一致性**:WIP Detail 未同步改成新 UI;但後端先支持新欄位,避免 overview drilldown 失真。
|
||||
- **快取不可用場景**:filter-options 需 fallback 查詢,首次延遲可能上升。
|
||||
|
||||
## Validation Plan
|
||||
|
||||
- 單元測試:
|
||||
- `tests/test_wip_routes.py`:新增 `meta/filter-options` 與新參數傳遞驗證
|
||||
- `tests/test_wip_service.py`:新增 filter-options 來源與新欄位索引輸出驗證
|
||||
- `frontend/tests/wip-derive.test.js`:CSV/新欄位 query 參數組裝驗證
|
||||
- 手動驗證:
|
||||
- 進入 `/wip-overview`,首次不查主資料也能看到下拉選項
|
||||
- 套用任一新舊篩選後 summary/matrix/hold 都一致變化
|
||||
- 下拉框可模糊搜尋、可多選、可清除
|
||||
@@ -0,0 +1,36 @@
|
||||
## Why
|
||||
|
||||
WIP 即時概況目前使用文字輸入搭配動態 autocomplete,使用者在多條件查詢時容易反覆輸入且選項不一致。改為可搜尋的下拉清單並新增 Wafer 維度篩選,可降低操作成本、提升查詢一致性,且能直接利用既有快取資料來源。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 將 WIP 即時概況篩選 UI 從文字 autocomplete 改為可模糊搜尋的下拉清單(對齊設備即時概況機台篩選互動)
|
||||
- 新增兩個篩選欄位:`Wafer LOT`(資料欄位 `FIRSTNAME`)、`Wafer Type`(資料欄位 `WAFERDESC`)
|
||||
- 新增 WIP 篩選選項 API,一次回傳舊有與新增篩選欄位的候選值,優先由 WIP 快取衍生索引提供
|
||||
- WIP 概況查詢 API(summary/matrix/hold)納入 `firstname`、`waferdesc` 參數
|
||||
- 前端初始化階段預先載入篩選選項(不需先觸發主查詢才有下拉選項)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_(none)_
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `wip-overview-page`: 篩選互動改為可搜尋下拉並新增 Wafer 維度篩選,且篩選選項由快取驅動
|
||||
|
||||
## Impact
|
||||
|
||||
- **Frontend**
|
||||
- `frontend/src/wip-overview/App.vue`
|
||||
- `frontend/src/wip-overview/components/FilterPanel.vue`
|
||||
- `frontend/src/wip-overview/style.css`
|
||||
- `frontend/src/core/wip-derive.js`
|
||||
- `frontend/tests/wip-derive.test.js`
|
||||
- **Backend**
|
||||
- `src/mes_dashboard/routes/wip_routes.py`
|
||||
- `src/mes_dashboard/services/wip_service.py`
|
||||
- `tests/test_wip_routes.py`
|
||||
- `tests/test_wip_service.py`
|
||||
- **No breaking route removal**: 既有 API 與參數仍保持相容,新增參數採選填。
|
||||
@@ -0,0 +1,39 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Overview page SHALL support dropdown filtering
|
||||
The page SHALL provide searchable dropdown filters for WORKORDER, LOT ID, PACKAGE, TYPE, Wafer LOT, and Wafer Type.
|
||||
|
||||
#### Scenario: Filter options preload from cache-backed endpoint
|
||||
- **WHEN** the page initializes
|
||||
- **THEN** the page SHALL call `GET /api/wip/meta/filter-options`
|
||||
- **THEN** dropdown options SHALL be loaded before user performs first query
|
||||
- **THEN** options SHALL include `workorders`, `lotids`, `packages`, `types`, `firstnames`, and `waferdescs`
|
||||
|
||||
#### Scenario: Searchable dropdown interaction
|
||||
- **WHEN** user opens any filter dropdown
|
||||
- **THEN** the dropdown SHALL support fuzzy keyword search over loaded options
|
||||
- **THEN** user SHALL be able to select one or multiple options
|
||||
|
||||
#### Scenario: Apply and clear filters
|
||||
- **WHEN** user clicks `套用篩選`
|
||||
- **THEN** all three API calls (`/api/wip/overview/summary`, `/api/wip/overview/matrix`, `/api/wip/overview/hold`) SHALL reload with selected filter values
|
||||
- **WHEN** user clicks `清除篩選`
|
||||
- **THEN** all filter values SHALL reset and data SHALL reload without filters
|
||||
|
||||
#### Scenario: Active filter chips
|
||||
- **WHEN** any filter has selected values
|
||||
- **THEN** selected values SHALL be displayed as removable chips
|
||||
- **THEN** removing a chip SHALL trigger data reload with updated filters
|
||||
|
||||
### Requirement: Overview page SHALL persist filter state in URL
|
||||
The page SHALL synchronize all filter state to URL query parameters as the single source of truth.
|
||||
|
||||
#### Scenario: URL state includes new wafer filters
|
||||
- **WHEN** filters are applied
|
||||
- **THEN** URL query parameters SHALL include non-empty values for `workorder`, `lotid`, `package`, `type`, `firstname`, `waferdesc`, and `status`
|
||||
- **THEN** multi-select values SHALL be serialized as comma-separated strings
|
||||
|
||||
#### Scenario: URL state restoration on load
|
||||
- **WHEN** the page is loaded with filter query parameters
|
||||
- **THEN** all filter controls SHALL restore values from URL
|
||||
- **THEN** data SHALL load with restored filters applied
|
||||
@@ -0,0 +1,25 @@
|
||||
## 1. OpenSpec alignment
|
||||
|
||||
- [x] 1.1 Confirm modified capability scope and spec deltas for `wip-overview-page`
|
||||
|
||||
## 2. Backend: cache-backed filter options + new filter fields
|
||||
|
||||
- [x] 2.1 Add WIP service support for `FIRSTNAME` / `WAFERDESC` in cache-derived indexes and snapshot filter path
|
||||
- [x] 2.2 Add cache-backed `get_wip_filter_options` service API returning workorders/lotids/packages/types/firstnames/waferdescs
|
||||
- [x] 2.3 Add `GET /api/wip/meta/filter-options` route
|
||||
- [x] 2.4 Extend overview query routes (`summary`, `matrix`, `hold`) to parse and pass `firstname` and `waferdesc`
|
||||
- [x] 2.5 Keep backward compatibility for existing params and behavior
|
||||
|
||||
## 3. Frontend: WIP overview filter UX replacement
|
||||
|
||||
- [x] 3.1 Replace `wip-overview` filter inputs with searchable dropdowns (reuse `resource-shared/components/MultiSelect.vue`)
|
||||
- [x] 3.2 Add two new filters in UI: `Wafer LOT` (`firstname`) and `Wafer Type` (`waferdesc`)
|
||||
- [x] 3.3 Load filter options from `/api/wip/meta/filter-options` on initialization and bind to dropdown options
|
||||
- [x] 3.4 Ensure apply/clear/chip-remove and URL sync all work with old + new filters
|
||||
|
||||
## 4. Tests and verification
|
||||
|
||||
- [x] 4.1 Update route tests for new endpoint and new query parameters
|
||||
- [x] 4.2 Update service tests for filter options and new index fields
|
||||
- [x] 4.3 Update frontend derive tests for URL/query param mapping
|
||||
- [x] 4.4 Run targeted test commands and fix regressions
|
||||
2
openspec/changes/reject-history-ui-polish/.openspec.yaml
Normal file
2
openspec/changes/reject-history-ui-polish/.openspec.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-22
|
||||
57
openspec/changes/reject-history-ui-polish/design.md
Normal file
57
openspec/changes/reject-history-ui-polish/design.md
Normal file
@@ -0,0 +1,57 @@
|
||||
## Context
|
||||
|
||||
The reject-history page is a monolithic `App.vue` (~968 lines template+script) with a co-located `style.css`. It was built quickly and works, but differs from the hold-history page (the maturity benchmark) in structure and several UI details. The hold-history page delegates to 7 sub-components and follows project-wide conventions (loading overlay, hover effects, Chinese pagination text, header refresh button).
|
||||
|
||||
The page imports `wip-shared/styles.css` for design tokens and global classes, and `resource-shared/components/MultiSelect.vue` for multi-select dropdowns — but also duplicates ~120 lines of MultiSelect CSS in its own `style.css`.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Match hold-history's visual baseline: loading overlay, table hover, Chinese pagination, header refresh button
|
||||
- Extract App.vue into sub-components following hold-history's proven pattern
|
||||
- Remove duplicated MultiSelect CSS
|
||||
- Keep all existing functionality and API interactions unchanged
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Changing column names or data display (user explicitly excluded #1)
|
||||
- Adding new features, APIs, or functional capabilities
|
||||
- Migrating to Tailwind or shared-ui components (page stays on wip-shared CSS)
|
||||
- Touching backend code
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Component extraction mirrors hold-history's architecture
|
||||
|
||||
Extract into 5 sub-components under `frontend/src/reject-history/components/`:
|
||||
|
||||
| Component | Responsibility | hold-history equivalent |
|
||||
|-----------|---------------|------------------------|
|
||||
| `FilterPanel.vue` | Filter grid, checkboxes, action buttons, active chips | `FilterBar.vue` |
|
||||
| `SummaryCards.vue` | 6 KPI cards with lane colors | `SummaryCards.vue` |
|
||||
| `TrendChart.vue` | Quantity trend bar chart (vue-echarts) | `DailyTrend.vue` |
|
||||
| `ParetoSection.vue` | Pareto chart + table side-by-side | `ReasonPareto.vue` |
|
||||
| `DetailTable.vue` | Detail table + pagination | `DetailTable.vue` |
|
||||
|
||||
**Rationale**: The hold-history pattern is proven and familiar to the team. Same granularity, same naming convention.
|
||||
|
||||
### D2: State stays in App.vue, components receive props + emit events
|
||||
|
||||
App.vue keeps all reactive state (`filters`, `summary`, `trend`, `pareto`, `detail`, `loading`, etc.) and API functions. Sub-components are presentational. This matches hold-history exactly and avoids over-engineering with composables for a single-page report.
|
||||
|
||||
### D3: Remove duplicated MultiSelect CSS, rely on resource-shared import chain
|
||||
|
||||
The MultiSelect component from `resource-shared/components/MultiSelect.vue` already bundles its own styles. The ~120 lines duplicated in `reject-history/style.css` (`.multi-select`, `.multi-select-trigger`, `.multi-select-dropdown`, etc.) can be deleted.
|
||||
|
||||
**Risk**: If some pages import MultiSelect without importing `resource-shared/styles.css`, they break. But reject-history doesn't import resource-shared/styles.css either — the MultiSelect component uses scoped styles or injects its own. Verify before deleting.
|
||||
|
||||
### D4: Loading overlay uses existing wip-shared pattern
|
||||
|
||||
Add `<div v-if="loading.initial" class="loading-overlay"><span class="loading-spinner"></span></div>` after the `.dashboard` div, identical to hold-history. The `.loading-overlay` and `.loading-spinner` classes are already defined in `wip-shared/styles.css`.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Risk] MultiSelect CSS deletion breaks styling** → Verify the MultiSelect component renders correctly after removing the duplicated CSS. If it doesn't, the component may need its own `<style scoped>` block or the import chain needs adjustment.
|
||||
- **[Risk] Extraction introduces subtle regressions** → Each component boundary is a potential data-flow bug. Mitigate by keeping the extraction mechanical: cut template section → paste into component → add props/emits.
|
||||
- **[Trade-off] No composable extraction** → The script logic stays in App.vue (400+ lines). This is acceptable for now — hold-history works the same way. Future refactoring can extract a `useRejectHistory` composable if needed.
|
||||
28
openspec/changes/reject-history-ui-polish/proposal.md
Normal file
28
openspec/changes/reject-history-ui-polish/proposal.md
Normal file
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
The reject-history page was shipped as a monolithic single-file implementation. While functional, it has visual inconsistencies with other mature report pages (hold-history, wip-overview) and is missing standard UX affordances. Aligning it now reduces user confusion when switching between report pages and improves maintainability.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add `tbody tr:hover` highlight and missing loading overlay/spinner to match hold-history baseline
|
||||
- Localize pagination controls from English (Prev/Next/Page/Total) to Chinese (上一頁/下一頁/頁/共)
|
||||
- Remove ~120 lines of duplicated MultiSelect CSS from `style.css` (already provided by `resource-shared/styles.css`)
|
||||
- Add a "重新整理" (refresh) button in the header, consistent with hold-history
|
||||
- Extract monolithic `App.vue` (~968 lines) into focused sub-components mirroring hold-history's architecture: `FilterPanel`, `SummaryCards`, `TrendChart`, `ParetoSection`, `DetailTable`
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_(none — no new functional capabilities are introduced)_
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `reject-history-page`: UI/UX polish — add loading overlay, hover effects, localized pagination, header refresh button, and modular component extraction
|
||||
|
||||
## Impact
|
||||
|
||||
- **Files modified**: `frontend/src/reject-history/App.vue`, `frontend/src/reject-history/style.css`
|
||||
- **Files created**: `frontend/src/reject-history/components/FilterPanel.vue`, `SummaryCards.vue`, `TrendChart.vue`, `ParetoSection.vue`, `DetailTable.vue`
|
||||
- **No API changes** — all backend endpoints remain untouched
|
||||
- **No dependency changes** — continues using `vue-echarts`, `resource-shared/MultiSelect`
|
||||
@@ -0,0 +1,78 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Reject History page SHALL provide filterable historical query controls
|
||||
The page SHALL provide a filter area for date range and major production dimensions to drive all report sections.
|
||||
|
||||
#### Scenario: Default filter values
|
||||
- **WHEN** the page is first loaded
|
||||
- **THEN** `start_date` and `end_date` SHALL default to a valid recent range
|
||||
- **THEN** all other dimension filters SHALL default to empty (no restriction)
|
||||
|
||||
#### Scenario: Apply and clear filters
|
||||
- **WHEN** user clicks "查詢"
|
||||
- **THEN** summary, trend, pareto, and list sections SHALL reload with the same filter set
|
||||
- **WHEN** user clicks "清除條件"
|
||||
- **THEN** all filters SHALL reset to defaults and all sections SHALL reload
|
||||
|
||||
#### Scenario: Required core filters are present
|
||||
- **WHEN** the filter panel is rendered
|
||||
- **THEN** it SHALL include `start_date/end_date` time filter controls
|
||||
- **THEN** it SHALL include reason filter control
|
||||
- **THEN** it SHALL include `WORKCENTER_GROUP` filter control
|
||||
|
||||
#### Scenario: Header refresh button
|
||||
- **WHEN** the page header is rendered
|
||||
- **THEN** it SHALL include a "重新整理" button in the header-right area
|
||||
- **WHEN** user clicks the refresh button
|
||||
- **THEN** all sections SHALL reload with current filters (equivalent to "查詢")
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Reject History page SHALL display a loading overlay during initial data load
|
||||
The page SHALL show a full-screen loading overlay with spinner during the first data load to provide clear feedback.
|
||||
|
||||
#### Scenario: Loading overlay on initial mount
|
||||
- **WHEN** the page first mounts and `loadAllData` begins
|
||||
- **THEN** a loading overlay with spinner SHALL be displayed over the page content
|
||||
- **WHEN** all initial API responses complete
|
||||
- **THEN** the overlay SHALL be hidden
|
||||
|
||||
#### Scenario: Subsequent queries do not show overlay
|
||||
- **WHEN** the user triggers a re-query after initial load
|
||||
- **THEN** no full-screen overlay SHALL appear (inline loading states are sufficient)
|
||||
|
||||
### Requirement: Detail table rows SHALL highlight on hover
|
||||
The detail table and pareto table rows SHALL visually respond to mouse hover for improved readability.
|
||||
|
||||
#### Scenario: Row hover in detail table
|
||||
- **WHEN** user hovers over a row in the detail table
|
||||
- **THEN** the row background SHALL change to a subtle highlight color
|
||||
|
||||
#### Scenario: Row hover in pareto table
|
||||
- **WHEN** user hovers over a row in the pareto summary table
|
||||
- **THEN** the row background SHALL change to a subtle highlight color
|
||||
|
||||
### Requirement: Pagination controls SHALL use Chinese labels
|
||||
The detail list pagination SHALL display controls in Chinese to match the rest of the page language.
|
||||
|
||||
#### Scenario: Pagination button labels
|
||||
- **WHEN** the pagination controls are rendered
|
||||
- **THEN** the previous-page button SHALL display "上一頁"
|
||||
- **THEN** the next-page button SHALL display "下一頁"
|
||||
- **THEN** the page info text SHALL use Chinese formatting (e.g., "第 1 / 5 頁 · 共 250 筆")
|
||||
|
||||
### Requirement: Reject History page SHALL be structured as modular sub-components
|
||||
The page template SHALL delegate sections to focused sub-components, following the hold-history architecture pattern.
|
||||
|
||||
#### Scenario: Component decomposition
|
||||
- **WHEN** the page source is examined
|
||||
- **THEN** the filter panel SHALL be a separate `FilterPanel.vue` component
|
||||
- **THEN** the KPI summary cards SHALL be a separate `SummaryCards.vue` component
|
||||
- **THEN** the trend chart SHALL be a separate `TrendChart.vue` component
|
||||
- **THEN** the pareto section (chart + table) SHALL be a separate `ParetoSection.vue` component
|
||||
- **THEN** the detail table with pagination SHALL be a separate `DetailTable.vue` component
|
||||
|
||||
#### Scenario: App.vue acts as orchestrator
|
||||
- **WHEN** the page runs
|
||||
- **THEN** `App.vue` SHALL hold all reactive state and API logic
|
||||
- **THEN** sub-components SHALL receive data via props and communicate via events
|
||||
26
openspec/changes/reject-history-ui-polish/tasks.md
Normal file
26
openspec/changes/reject-history-ui-polish/tasks.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## 1. Quick visual fixes (no component extraction needed)
|
||||
|
||||
- [x] 1.1 Add `tbody tr:hover` background rule to `style.css` for `.detail-table` and `.pareto-table`
|
||||
- [x] 1.2 Localize pagination: change "Prev" → "上一頁", "Next" → "下一頁", "Page X / Y · Total Z" → "第 X / Y 頁 · 共 Z 筆"
|
||||
- [x] 1.3 Add loading overlay + spinner after `.dashboard` div (`<div v-if="loading.initial" class="loading-overlay">`)
|
||||
- [x] 1.4 Add "重新整理" button in header-right area, wired to `applyFilters`
|
||||
- [x] 1.5 Remove duplicated MultiSelect CSS (~120 lines of `.multi-select-*` rules) from `style.css`; verify MultiSelect still renders correctly
|
||||
|
||||
## 2. Extract sub-components from App.vue
|
||||
|
||||
- [x] 2.1 Create `components/FilterPanel.vue` — extract filter grid, checkbox row, action buttons, and active-filter chips section; props: `filters`, `options`, `loading`, `activeFilterChips`; emits: `apply`, `clear`, `remove-chip`, `export-csv`, `pareto-scope-toggle`
|
||||
- [x] 2.2 Create `components/SummaryCards.vue` — extract `.summary-row` section; props: `cards`
|
||||
- [x] 2.3 Create `components/TrendChart.vue` — extract trend chart `.card` section with ECharts registration and chart option computed internally; props: `items`, `loading`
|
||||
- [x] 2.4 Create `components/ParetoSection.vue` — extract pareto chart + table `.card` section with ECharts registration and chart option computed internally; props: `items`, `detailReason`, `loading`; emits: `reason-click`
|
||||
- [x] 2.5 Create `components/DetailTable.vue` — extract detail table + pagination; props: `items`, `pagination`, `loading`; emits: `go-to-page`
|
||||
|
||||
## 3. Rewire App.vue as orchestrator
|
||||
|
||||
- [x] 3.1 Replace inline template sections with sub-component tags, passing props and wiring emits
|
||||
- [x] 3.2 Move ECharts `use()` registration and chart computed properties into their respective chart components
|
||||
- [x] 3.3 Verify all interactions work: filter apply/clear, pareto click → detail filter, pagination, CSV export, refresh button
|
||||
|
||||
## 4. Verify and build
|
||||
|
||||
- [x] 4.1 Run `vite build` and confirm no compilation errors
|
||||
- [x] 4.2 Visually verify: loading overlay, table hover, Chinese pagination, refresh button, pareto interaction, filter chips
|
||||
Reference in New Issue
Block a user