refactor: simplify order sidebar

This commit is contained in:
czc
2026-07-22 10:01:53 +08:00
parent 658a23b630
commit 60ba969e37
4 changed files with 57 additions and 105 deletions
-1
View File
@@ -207,7 +207,6 @@ export function Dashboard() {
selectedOrderId={selectedOrder.id} selectedOrderId={selectedOrder.id}
search={search} search={search}
activeOrder={selectedOrder} activeOrder={selectedOrder}
showProjectDossier={orders.length === 1}
onSearchChange={setSearch} onSearchChange={setSearch}
onSelectOrder={handleSelectOrder} onSelectOrder={handleSelectOrder}
/> />
@@ -0,0 +1,46 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
import { createMockOrders } from '@/data/mock-data'
import { OrderSidebar } from './OrderSidebar'
describe('OrderSidebar', () => {
const sourceOrders = createMockOrders()
const activeOrder = sourceOrders[0]
it('keeps the order list and active BOM dossier without legacy filters or pagination', () => {
const markup = renderToStaticMarkup(
<OrderSidebar
orders={sourceOrders.slice(0, 3)}
activeOrder={activeOrder}
selectedOrderId={activeOrder.id}
search=""
onSearchChange={() => undefined}
onSelectOrder={() => undefined}
/>,
)
expect(markup).toContain('订单列表')
expect(markup).toContain('项目 BOM 档案')
expect(markup).not.toContain('筛选订单')
expect(markup).not.toContain('全部状态')
expect(markup).not.toContain('全部产线')
expect(markup).not.toContain('订单分页')
})
it('keeps the active BOM dossier when the search result is empty', () => {
const markup = renderToStaticMarkup(
<OrderSidebar
orders={[]}
activeOrder={activeOrder}
selectedOrderId={activeOrder.id}
search="missing"
onSearchChange={() => undefined}
onSelectOrder={() => undefined}
/>,
)
expect(markup).toContain('没有匹配订单')
expect(markup).toContain('修改搜索关键词后重试')
expect(markup).toContain('项目 BOM 档案')
})
})
+5 -37
View File
@@ -1,16 +1,12 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { import {
Boxes, Boxes,
ChevronDown,
ChevronLeft,
ChevronRight,
DatabaseZap, DatabaseZap,
GitBranch, GitBranch,
Layers3, Layers3,
PackageOpen, PackageOpen,
Search, Search,
ShieldAlert, ShieldAlert,
SlidersHorizontal,
} from 'lucide-react' } from 'lucide-react'
import type { OrderSummary, RiskLevel } from '@/types' import type { OrderSummary, RiskLevel } from '@/types'
import { calculateOrderTreeStats, riskMeta, statusMeta } from '@/lib/production' import { calculateOrderTreeStats, riskMeta, statusMeta } from '@/lib/production'
@@ -21,7 +17,6 @@ import { RingProgress, TechVisual } from './TechVisual'
interface OrderSidebarProps { interface OrderSidebarProps {
orders: OrderSummary[] orders: OrderSummary[]
activeOrder: OrderSummary activeOrder: OrderSummary
showProjectDossier: boolean
selectedOrderId: string selectedOrderId: string
search: string search: string
onSearchChange: (value: string) => void onSearchChange: (value: string) => void
@@ -31,39 +26,24 @@ interface OrderSidebarProps {
export function OrderSidebar({ export function OrderSidebar({
orders, orders,
activeOrder, activeOrder,
showProjectDossier,
selectedOrderId, selectedOrderId,
search, search,
onSearchChange, onSearchChange,
onSelectOrder, onSelectOrder,
}: OrderSidebarProps) { }: OrderSidebarProps) {
return ( return (
<aside className={`dashboard-panel sidebar-panel ${showProjectDossier ? 'has-project-dossier' : ''}`}> <aside className="dashboard-panel sidebar-panel has-project-dossier">
<div className="sidebar-heading"> <div className="sidebar-heading">
<PanelTitle icon={<Boxes />} title="订单总览" subtitle={`${orders.length} 个订单`} /> <PanelTitle icon={<Boxes />} title="订单列表" subtitle={`${orders.length} 个订单`} />
<button type="button" className="icon-tool-button" aria-label="筛选订单">
<SlidersHorizontal />
<span></span>
</button>
</div> </div>
<div className="sidebar-filter-card"> <div className="sidebar-filter-card">
<div className="select-row">
<button type="button" className="select-chip">
<span></span>
<ChevronDown />
</button>
<button type="button" className="select-chip">
<span>线</span>
<ChevronDown />
</button>
</div>
<label className="search-field"> <label className="search-field">
<Search aria-hidden="true" /> <Search aria-hidden="true" />
<Input <Input
value={search} value={search}
onChange={(event) => onSearchChange(event.target.value)} onChange={(event) => onSearchChange(event.target.value)}
placeholder="搜索订单号/产品" placeholder="搜索订单号/产品/产线"
aria-label="搜索订单" aria-label="搜索订单"
/> />
</label> </label>
@@ -113,24 +93,12 @@ export function OrderSidebar({
<div className="empty-state"> <div className="empty-state">
<ShieldAlert aria-hidden="true" /> <ShieldAlert aria-hidden="true" />
<strong></strong> <strong></strong>
<span></span> <span></span>
</div> </div>
) : null} ) : null}
</div> </div>
{showProjectDossier ? <ProjectDossier order={activeOrder} /> : ( <ProjectDossier order={activeOrder} />
<div className="sidebar-pagination" aria-label="订单分页">
<button type="button" aria-label="上一页"><ChevronLeft /></button>
<button type="button" className="is-active">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">4</button>
<button type="button">5</button>
<span>...</span>
<button type="button">22</button>
<button type="button" aria-label="下一页"><ChevronRight /></button>
</div>
)}
</aside> </aside>
) )
} }
+6 -67
View File
@@ -859,10 +859,7 @@ svg {
line-height: 1.2; line-height: 1.2;
} }
.icon-tool-button, .flow-toolbar button {
.select-chip,
.flow-toolbar button,
.sidebar-pagination button {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -880,24 +877,12 @@ svg {
background 180ms ease; background 180ms ease;
} }
.icon-tool-button { .flow-toolbar svg {
padding: 0 9px;
font-family: var(--font-sans);
font-size: 12px;
}
.icon-tool-button svg,
.select-chip svg,
.flow-toolbar svg,
.sidebar-pagination svg {
width: 15px; width: 15px;
height: 15px; height: 15px;
} }
.icon-tool-button:hover, .flow-toolbar button:hover {
.select-chip:hover,
.flow-toolbar button:hover,
.sidebar-pagination button:hover {
color: var(--foreground); color: var(--foreground);
border-color: var(--border-strong); border-color: var(--border-strong);
background: background:
@@ -911,24 +896,10 @@ svg {
margin-bottom: 8px; margin-bottom: 8px;
} }
.select-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 7px;
}
.select-chip {
justify-content: space-between;
width: 100%;
padding: 0 10px;
font-family: var(--font-sans);
font-size: 11px;
}
.search-field { .search-field {
position: relative; position: relative;
display: block; display: block;
margin-top: 7px; margin-top: 0;
} }
.search-field svg { .search-field svg {
@@ -1103,36 +1074,6 @@ svg {
height: 43px; height: 43px;
} }
.sidebar-pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
flex-shrink: 0;
min-height: 28px;
margin-top: 5px;
}
.sidebar-pagination button {
min-width: 25px;
min-height: 25px;
padding: 0 6px;
font-family: var(--font-mono);
font-size: 11px;
}
.sidebar-pagination button.is-active {
color: #ecfeff;
border-color: var(--cyan);
background: linear-gradient(180deg, rgba(47, 148, 255, 0.78), rgba(19, 200, 246, 0.34));
}
.sidebar-pagination span {
color: var(--muted);
font-family: var(--font-mono);
font-size: 12px;
}
.flow-panel-head { .flow-panel-head {
position: relative; position: relative;
justify-content: space-between; justify-content: space-between;
@@ -3229,8 +3170,8 @@ svg {
} }
.has-project-dossier .order-list { .has-project-dossier .order-list {
flex: 0 0 auto; flex: 0 1 auto;
max-height: 96px; max-height: 42%;
} }
.has-project-dossier .order-row { .has-project-dossier .order-row {
@@ -4067,7 +4008,6 @@ svg {
.panel-title h2, .panel-title h2,
.mini-panel-head h2, .mini-panel-head h2,
.flow-panel-head h2 { font-size: var(--text-panel); } .flow-panel-head h2 { font-size: var(--text-panel); }
.has-project-dossier .order-list { max-height: 110px; }
.has-project-dossier .order-row { height: 100px; } .has-project-dossier .order-row { height: 100px; }
.project-stat-grid > div { min-height: 44px; } .project-stat-grid > div { min-height: 44px; }
} }
@@ -4112,7 +4052,6 @@ svg {
.mini-panel-head p { font-size: 14px; } .mini-panel-head p { font-size: 14px; }
.sidebar-panel, .sidebar-panel,
.detail-panel { padding: 12px; } .detail-panel { padding: 12px; }
.has-project-dossier .order-list { max-height: 136px; }
.has-project-dossier .order-row { height: 122px; } .has-project-dossier .order-row { height: 122px; }
.order-row-main { height: 86px; grid-template-rows: repeat(4, minmax(18px, auto)); } .order-row-main { height: 86px; grid-template-rows: repeat(4, minmax(18px, auto)); }
.order-row-titleline strong { font-size: 15px; } .order-row-titleline strong { font-size: 15px; }