diff --git a/design-qa.md b/design-qa.md index 07cc8a3..741649a 100644 --- a/design-qa.md +++ b/design-qa.md @@ -1,34 +1,34 @@ # Design QA -- Source visual truth: `/workspace/scratch/6e52996114b8/upload/57970687-58ad-46f8-9977-15ec608a8b12.png` -- Source pixel dimensions: `504 × 158`. -- Intended implementation viewport: dark-theme mobile workspace, focused content width around `393px` CSS pixels. +- Source visual truth: `/workspace/scratch/6e52996114b8/upload/661cd6c2-dfc4-46ea-ac8b-17630be6c35e.png` +- Source pixel dimensions: `462 × 273`. +- Intended implementation viewport: dark-theme mobile workspace, focused content width around `360px` CSS pixels. - Implementation screenshot: unavailable. - CSS size and density normalization: unavailable because the implementation could not be opened in the cloud browser. -- State: 形态图片页默认筛选区,四个图标操作可见。 +- State: 形态图片页包含镜头定位与完整筛选区。 ## Full-view comparison evidence -The source crop shows the search and type controls on the first row, while the second row combines a wide status select with four icon buttons. The second row is visually crowded and leaves the controls competing for the same horizontal space. +The source crop shows the prior three-row mobile filter layout: search on its own row, two selects on the next row, and four icons on a third row. The user requested a denser two-row arrangement. -The mobile implementation now uses three explicit rows at container widths up to `460px`: full-width search, two equal-width selects, then a full-width right-aligned icon action row. The existing `10px` internal rhythm and icon ordering are preserved. +The implementation now uses a wrapping flex layout at container widths up to `460px`. Search and type occupy the first line; status and the four-icon action group occupy the second line. The status select can shrink from `110px` to `90px`, while the icon group keeps its required width and aligns right. A local Sites preview started successfully, but the cloud browser rejected `terminal.local` with `net::ERR_BLOCKED_BY_CLIENT`. Therefore no browser-rendered implementation screenshot is available. ## Focused region comparison evidence -- Row 1: search spans both grid columns. -- Row 2: subject type and image status use equal-width `minmax(0, 1fr)` columns. -- Row 3: the four icon actions occupy the complete row and align to the right. -- Spacing: both column and row gaps are `10px`. -- Accessibility and behavior: filter labels, layout switching, refresh, batch action, and mobile-disabled popovers remain unchanged. +- Row 1: search flexes into remaining space; type remains `120px` wide. +- Row 2: status starts at `110px` and may shrink to `90px`; icon controls reserve at least `152px` and consume remaining width. +- Spacing: both rows use a shared `10px` gap. +- Overflow protection: status is the only shrinking control on the second row, so the four icon buttons remain complete. +- Accessibility and behavior: filter labels, layout switching, refresh, batch action, and mobile-disabled popovers are unchanged. These are code-level checks only and do not replace a rendered focused-region comparison. ## Required fidelity surfaces - Fonts and typography: no typography or truncation rules changed. -- Spacing and layout rhythm: mobile controls now follow a stable three-row grid instead of compressing the status select and actions together. +- Spacing and layout rhythm: the filter area returns to two compact rows with a consistent `10px` gap. - Colors and visual tokens: existing dark-theme surface and control tokens are unchanged. - Image quality and asset fidelity: no image behavior or assets changed. - Copy and content: all search, type, status, and icon action labels are preserved. @@ -38,12 +38,12 @@ These are code-level checks only and do not replace a rendered focused-region co - [P1] Browser-rendered comparison unavailable - Location: 形态图片移动端筛选区。 - Evidence: the required cloud browser returns `net::ERR_BLOCKED_BY_CLIENT` for the healthy local preview. - - Impact: exact responsive wrapping, control heights, touch spacing, and console state cannot be visually certified. - - Fix: capture the page in an available browser near `393px` content width and compare the three-row filter layout. + - Impact: exact wrapping, control widths, touch spacing, and console state cannot be visually certified. + - Fix: capture the page in an available browser near `360px` content width and compare the two-row filter layout. ## Primary interactions tested -Automated tests verify the mobile two-column grid, full-width search row, equal select row, full-width right-aligned icon row, filter behavior, icon ordering, layout switching, and refresh workflow. All 275 tests pass. +Automated tests verify the mobile wrapping flex layout, search/type sizing, shrinkable status width, protected icon-group width, filter behavior, icon ordering, layout switching, and refresh workflow. All 275 tests pass. ## Console errors checked @@ -51,14 +51,14 @@ Not checked because the page could not be opened in the cloud browser. ## Comparison history -- Iteration 1: the source identified crowding between the status filter and the four icon actions. -- Iteration 2: separated mobile controls into search, equal filters, and action rows; the full test/build suite passed, while browser capture remained blocked. +- Iteration 1: the previous three-row layout avoided crowding but used more vertical space than requested. +- Iteration 2: consolidated the controls into two rows and made only the status select shrinkable; the full test/build suite passed, while browser capture remained blocked. ## Implementation checklist -- [x] Give mobile search the full first row. -- [x] Give type and status filters equal widths on the second row. -- [x] Move four icon actions to a dedicated right-aligned third row. +- [x] Keep search and type on the first mobile row. +- [x] Keep status and all four icons on the second mobile row. +- [x] Allow status to shrink before icon controls are affected. - [x] Preserve existing filter and action behavior. - [ ] Capture and compare the rendered mobile state in the cloud browser. diff --git a/src/features/subject-images/SubjectImagesPage.vue b/src/features/subject-images/SubjectImagesPage.vue index 524fe3c..095295d 100644 --- a/src/features/subject-images/SubjectImagesPage.vue +++ b/src/features/subject-images/SubjectImagesPage.vue @@ -966,26 +966,25 @@ async function showStaleForms() { } @container (max-width: 460px) { .form-image-filters { - grid-template-columns: repeat(2, minmax(0, 1fr)); + display: flex; + flex-wrap: wrap; gap: 10px; } - /* 移动端让搜索独占首行,两个筛选项等宽,图标操作独占末行,避免控件互相挤压。 */ + /* 移动端固定为两行:搜索给类型选择让位,状态可收缩以优先保留四个完整操作按钮。 */ .search-filter { - grid-column: 1 / -1; - grid-row: 1; + flex: 1 1 calc(100% - 130px); + min-width: 0; } .module-filter { - grid-column: 1; - grid-row: 2; + flex: 0 0 120px; } .status-filter { - grid-column: 2; - grid-row: 2; + flex: 0 1 110px; + min-width: 90px; } .form-image-display-controls { - grid-column: 1 / -1; - grid-row: 3; - width: 100%; + flex: 1 0 152px; + width: auto; justify-content: flex-end; } .filter-result-count { diff --git a/tests/components/ui/flatTheme.test.ts b/tests/components/ui/flatTheme.test.ts index 521b5e1..c4d7c1b 100644 --- a/tests/components/ui/flatTheme.test.ts +++ b/tests/components/ui/flatTheme.test.ts @@ -208,13 +208,13 @@ describe('全站平面主题', () => { expect(css).toMatch(/\.asset-impact-context\s*\{[^}]*padding(?:-block)?:\s*5px/) expect(css).toMatch(/\.asset-impact-context\s*\{[^}]*margin:\s*0/) expect(css).toMatch( - /@container \(max-width: 460px\)\s*\{[^}]*\.form-image-filters\s*\{[^}]*grid-template-columns:\s*repeat\(2, minmax\(0, 1fr\)\);[^}]*gap:\s*10px;/ + /@container \(max-width: 460px\)\s*\{[^}]*\.form-image-filters\s*\{[^}]*display:\s*flex;[^}]*flex-wrap:\s*wrap;[^}]*gap:\s*10px;/ ) - expect(css).toMatch(/\.search-filter\s*\{[^}]*grid-column:\s*1 \/ -1;[^}]*grid-row:\s*1;/) - expect(css).toMatch(/\.module-filter\s*\{[^}]*grid-column:\s*1;[^}]*grid-row:\s*2;/) - expect(css).toMatch(/\.status-filter\s*\{[^}]*grid-column:\s*2;[^}]*grid-row:\s*2;/) + expect(css).toMatch(/\.search-filter\s*\{[^}]*flex:\s*1 1 calc\(100% - 130px\);[^}]*min-width:\s*0;/) + expect(css).toMatch(/\.module-filter\s*\{[^}]*flex:\s*0 0 120px;/) + expect(css).toMatch(/\.status-filter\s*\{[^}]*flex:\s*0 1 110px;[^}]*min-width:\s*90px;/) expect(css).toMatch( - /\.form-image-display-controls\s*\{[^}]*grid-column:\s*1 \/ -1;[^}]*grid-row:\s*3;[^}]*width:\s*100%;[^}]*justify-content:\s*flex-end;/ + /\.form-image-display-controls\s*\{[^}]*flex:\s*1 0 152px;[^}]*width:\s*auto;[^}]*justify-content:\s*flex-end;/ ) expect(css).toMatch(/\.filter-result-count\s*\{[^}]*display:\s*none;/) expect(css).toMatch(