feat(platform): polish customer time window controls

This commit is contained in:
lingniu
2026-07-05 20:54:04 +08:00
parent 2b246f414e
commit 68c1c7cef4
3 changed files with 90 additions and 24 deletions

View File

@@ -294,30 +294,46 @@ export function AppShell({
value={keyword} value={keyword}
onChange={setKeyword} onChange={setKeyword}
onEnterPress={search} onEnterPress={search}
style={{ width: 320 }} style={{ width: 280 }}
/> />
<Button theme="solid" type="primary" loading={searching} onClick={search}></Button> <Button theme="solid" type="primary" loading={searching} onClick={search}></Button>
</Space> </Space>
<div className="vp-topbar-context"> <div className="vp-topbar-context">
<div className="vp-topbar-time-window" aria-label="客户时间窗"> <div className="vp-topbar-time-window" aria-label="客户时间窗">
<label> <div className="vp-topbar-time-window-copy">
<span></span> <strong></strong>
<span></span>
</div>
<label htmlFor="vp-customer-time-window-start">
<span></span>
<input <input
aria-label="顶部时间窗开始" id="vp-customer-time-window-start"
type="date" aria-label="客户时间窗开始日期"
type="text"
inputMode="numeric"
pattern="\d{4}-\d{2}-\d{2}"
placeholder="YYYY-MM-DD"
maxLength={10}
value={dateFrom} value={dateFrom}
onChange={(event) => onCustomerTimeWindowChange?.({ dateFrom: event.currentTarget.value, dateTo })} onChange={(event) => onCustomerTimeWindowChange?.({ dateFrom: event.currentTarget.value, dateTo })}
/> />
</label> </label>
<label> <label className="vp-sr-only" htmlFor="vp-customer-time-window-start"></label>
<span></span> <label htmlFor="vp-customer-time-window-end">
<span></span>
<input <input
aria-label="顶部时间窗结束" id="vp-customer-time-window-end"
type="date" aria-label="客户时间窗结束日期"
type="text"
inputMode="numeric"
pattern="\d{4}-\d{2}-\d{2}"
placeholder="YYYY-MM-DD"
maxLength={10}
value={dateTo} value={dateTo}
onChange={(event) => onCustomerTimeWindowChange?.({ dateFrom, dateTo: event.currentTarget.value })} onChange={(event) => onCustomerTimeWindowChange?.({ dateFrom, dateTo: event.currentTarget.value })}
/> />
</label> </label>
<label className="vp-sr-only" htmlFor="vp-customer-time-window-end"></label>
<div className="vp-topbar-time-window-presets"> <div className="vp-topbar-time-window-presets">
<button type="button" aria-label="顶部时间窗 今天" onClick={() => applyTimeWindowPreset(1)}></button> <button type="button" aria-label="顶部时间窗 今天" onClick={() => applyTimeWindowPreset(1)}></button>
<button type="button" aria-label="顶部时间窗 近7天" onClick={() => applyTimeWindowPreset(7)}>7</button> <button type="button" aria-label="顶部时间窗 近7天" onClick={() => applyTimeWindowPreset(7)}>7</button>

View File

@@ -238,13 +238,25 @@ body {
min-width: 0; min-width: 0;
} }
.vp-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.vp-topbar { .vp-topbar {
height: var(--vp-shell-header); min-height: 72px;
display: grid; display: grid;
grid-template-columns: minmax(380px, 0.8fr) minmax(0, 1.2fr); grid-template-columns: minmax(350px, 0.62fr) minmax(0, 1.38fr);
align-items: center; align-items: center;
gap: 16px; gap: 12px;
padding: 0 24px; padding: 8px 24px;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
border-bottom: 1px solid var(--vp-border); border-bottom: 1px solid var(--vp-border);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
@@ -261,26 +273,55 @@ body {
min-width: 0; min-width: 0;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; align-content: center;
gap: 8px; justify-content: flex-start;
white-space: nowrap; gap: 6px;
overflow: hidden;
white-space: normal;
flex-wrap: wrap;
} }
.vp-topbar-time-window { .vp-topbar-time-window {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 4px; gap: 6px;
padding: 2px; min-width: 0;
max-width: 100%;
padding: 4px 6px;
border: 1px solid var(--vp-border); border: 1px solid var(--vp-border);
border-radius: 8px; border-radius: 8px;
background: #f8fafc; background: #f8fafc;
} }
.vp-topbar-time-window-copy {
display: inline-flex;
flex-direction: column;
justify-content: center;
width: 136px;
flex: 0 0 auto;
line-height: 1.1;
}
.vp-topbar-time-window-copy strong {
color: var(--vp-text);
font-size: 12px;
font-weight: 700;
}
.vp-topbar-time-window-copy span {
max-width: 136px;
overflow: hidden;
color: var(--vp-text-muted);
font-size: 10px;
text-overflow: ellipsis;
}
.vp-topbar-time-window-presets { .vp-topbar-time-window-presets {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 2px; gap: 2px;
padding-left: 2px; padding-left: 2px;
flex: 0 0 auto;
} }
.vp-topbar-time-window-presets button { .vp-topbar-time-window-presets button {
@@ -305,20 +346,23 @@ body {
.vp-topbar-time-window label { .vp-topbar-time-window label {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
flex: 0 0 auto;
gap: 4px; gap: 4px;
height: 28px; height: 28px;
padding: 0 6px; padding: 0 4px 0 6px;
border-radius: 6px;
background: #ffffff;
color: var(--vp-text-muted); color: var(--vp-text-muted);
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
} }
.vp-topbar-time-window input { .vp-topbar-time-window input {
width: 118px; width: 114px;
height: 24px; height: 24px;
border: 0; border: 0;
border-radius: 6px; border-radius: 4px;
background: #ffffff; background: #f8fafc;
color: var(--vp-text); color: var(--vp-text);
font-size: 12px; font-size: 12px;
outline: none; outline: none;
@@ -332,6 +376,7 @@ body {
height: 32px; height: 32px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
flex: 0 0 auto;
padding: 2px; padding: 2px;
border: 1px solid var(--vp-border); border: 1px solid var(--vp-border);
border-radius: 8px; border-radius: 8px;

View File

@@ -6706,9 +6706,14 @@ test('topbar customer time window scopes vehicle service tasks', async () => {
render(<App />); render(<App />);
expect(screen.getByText('客户时间窗')).toBeInTheDocument();
expect(screen.getByText('同步约束地图、轨迹、统计、导出和告警')).toBeInTheDocument();
expect(screen.getByLabelText('客户时间窗开始日期')).toHaveAttribute('placeholder', 'YYYY-MM-DD');
expect(screen.getByLabelText('客户时间窗结束日期')).toHaveAttribute('placeholder', 'YYYY-MM-DD');
fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤A时间窗' } }); fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤A时间窗' } });
fireEvent.change(screen.getByLabelText('顶部时间窗开始'), { target: { value: '2026-07-01' } }); fireEvent.change(screen.getByLabelText('客户时间窗开始日期'), { target: { value: '2026-07-01' } });
fireEvent.change(screen.getByLabelText('顶部时间窗结束'), { target: { value: '2026-07-03' } }); fireEvent.change(screen.getByLabelText('客户时间窗结束日期'), { target: { value: '2026-07-03' } });
fireEvent.click(screen.getByRole('button', { name: '查询车辆' })); fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
expect(await screen.findByText('粤A时间窗 / VIN-TOPBAR-WINDOW')).toBeInTheDocument(); expect(await screen.findByText('粤A时间窗 / VIN-TOPBAR-WINDOW')).toBeInTheDocument();