feat(web): add visible batch vehicle search
This commit is contained in:
@@ -16,6 +16,26 @@ const statuses = ['', 'online', 'offline', 'driving', 'idle'];
|
||||
const EMPTY_VEHICLES: VehicleRealtimeRow[] = [];
|
||||
const MOBILE_MONITOR_QUERY = '(max-width: 760px)';
|
||||
|
||||
function BatchVehicleSearchDialog({ initialValue, onApply, onClose }: { initialValue: string; onApply: (value: string) => void; onClose: () => void }) {
|
||||
const [draft, setDraft] = useState(initialValue);
|
||||
const terms = useMemo(() => parseMonitorSearchTerms(draft), [draft]);
|
||||
useEffect(() => {
|
||||
const closeOnEscape = (event: KeyboardEvent) => { if (event.key === 'Escape') onClose(); };
|
||||
window.addEventListener('keydown', closeOnEscape);
|
||||
return () => window.removeEventListener('keydown', closeOnEscape);
|
||||
}, [onClose]);
|
||||
|
||||
return <div className="v2-batch-search-backdrop" role="presentation" onMouseDown={(event) => { if (event.target === event.currentTarget) onClose(); }}>
|
||||
<section className="v2-batch-search-dialog" role="dialog" aria-modal="true" aria-labelledby="batch-search-title">
|
||||
<header><div><strong id="batch-search-title">批量搜索车辆</strong><span>从 Excel、文本或聊天记录中直接粘贴车牌</span></div><button type="button" onClick={onClose} aria-label="关闭批量搜索"><IconClose /></button></header>
|
||||
<label htmlFor="batch-vehicle-search">每行一个车牌,也支持空格、逗号或分号分隔</label>
|
||||
<textarea id="batch-vehicle-search" autoFocus value={draft} onChange={(event) => setDraft(event.target.value)} placeholder={'粤A12345\n粤B67890\n粤C24680'} />
|
||||
<div className="v2-batch-search-summary"><span>已识别 <strong>{terms.length}</strong> 辆,重复项已自动去除</span><em>最多 {MAX_MONITOR_SEARCH_TERMS} 辆</em></div>
|
||||
<footer><button type="button" className="v2-secondary-button" onClick={onClose}>取消</button><button type="button" className="v2-primary-button" disabled={!terms.length} onClick={() => onApply(terms.join(','))}>应用搜索({terms.length})</button></footer>
|
||||
</section>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function useMobileMonitorLayout() {
|
||||
const [mobile, setMobile] = useState(() => window.matchMedia(MOBILE_MONITOR_QUERY).matches);
|
||||
useEffect(() => {
|
||||
@@ -181,6 +201,7 @@ export default function MonitorPage() {
|
||||
const [listOffset, setListOffset] = useState(0);
|
||||
const [listLimit, setListLimit] = useState(50);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [batchSearchOpen, setBatchSearchOpen] = useState(false);
|
||||
const deferredKeyword = useDeferredValue(keyword);
|
||||
const searchTerms = useMemo(() => parseMonitorSearchTerms(keyword), [keyword]);
|
||||
const [protocol, setProtocol] = useState('');
|
||||
@@ -255,7 +276,7 @@ export default function MonitorPage() {
|
||||
return (
|
||||
<div className="v2-monitor-page">
|
||||
<section className="v2-filterbar" aria-label="车辆筛选">
|
||||
<label className={`v2-search-field${searchTerms.length > 1 ? ' is-batch' : ''}`}>
|
||||
<div className={`v2-search-field${searchTerms.length > 1 ? ' is-batch' : ''}`}>
|
||||
<IconSearch />
|
||||
<input
|
||||
aria-label="搜索车辆"
|
||||
@@ -270,8 +291,9 @@ export default function MonitorPage() {
|
||||
}}
|
||||
placeholder="车牌 / VIN;可批量粘贴车牌"
|
||||
/>
|
||||
<button type="button" className="v2-search-batch-action" onClick={() => setBatchSearchOpen(true)}>批量</button>
|
||||
{searchTerms.length > 1 ? <span className={`v2-search-batch-count${batchMatch.missing.length && !batchSearchPending ? ' has-missing' : ''}`} aria-live="polite" title={searchTerms.length === MAX_MONITOR_SEARCH_TERMS ? `最多支持 ${MAX_MONITOR_SEARCH_TERMS} 条;${batchStatusTitle}` : batchStatusTitle}>{batchSearchPending ? `已识别 ${searchTerms.length} 辆` : `已找到 ${batchMatch.matched}/${searchTerms.length}`}</span> : null}
|
||||
</label>
|
||||
</div>
|
||||
<select value={protocol} onChange={(event) => { setProtocol(event.target.value); setListOffset(0); }} aria-label="协议">
|
||||
{protocols.map((item) => <option key={item} value={item}>{item || '全部协议'}</option>)}
|
||||
</select>
|
||||
@@ -283,6 +305,7 @@ export default function MonitorPage() {
|
||||
<div className="v2-monitor-mode" aria-label="监控视图"><button type="button" className={mode === 'map' ? 'is-active' : ''} onClick={() => setMode('map')}><IconMapPin />地图</button><button type="button" className={mode === 'list' ? 'is-active' : ''} onClick={() => { setMode('list'); setDetailOpen(false); }}><IconList />列表</button></div>
|
||||
<button type="button" className="v2-monitor-mobile-entry" onClick={() => setMobileEntryOpen(true)}><IconQrCode />手机端</button>
|
||||
</section>
|
||||
{batchSearchOpen ? <BatchVehicleSearchDialog initialValue={searchTerms.join('\n')} onClose={() => setBatchSearchOpen(false)} onApply={(value) => { setKeyword(value); setListOffset(0); setBatchSearchOpen(false); }} /> : null}
|
||||
|
||||
<section className="v2-kpis" aria-label="车辆整体统计">
|
||||
{[
|
||||
|
||||
Reference in New Issue
Block a user