feat(platform): polish customer time window controls
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user