refine Semi UI vehicle discovery workspace
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
IconAlarm, IconArrowRight, IconBox, IconCalendar, IconClock, IconCopy,
|
||||
IconChevronRight, IconMapPin, IconSearch, IconTickCircle
|
||||
IconChevronRight, IconMapPin, IconRefresh, IconSearch, IconTickCircle
|
||||
} from '@douyinfe/semi-icons';
|
||||
import { Button, Card, Descriptions, Empty, Input, List, Select, Table, Tag, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Card, Descriptions, Empty, Input, List, Select, Table, Tag } from '@douyinfe/semi-ui';
|
||||
import { FormEvent, lazy, Suspense, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { api } from '../../api/client';
|
||||
@@ -17,10 +17,12 @@ import { isValidAMapCoordinate } from '../../integrations/amap';
|
||||
import { FleetMap } from '../map/FleetMap';
|
||||
import { InlineError, PageLoading } from '../shared/AsyncState';
|
||||
import { MonitorReturnBar } from '../shared/MonitorReturnBar';
|
||||
import { PageHeader } from '../shared/PageHeader';
|
||||
import { SegmentedTabs } from '../shared/SegmentedTabs';
|
||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
||||
import { VehicleSourceEvidencePanel } from '../shared/VehicleSourceEvidencePanel';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { monitorReturnFromParams, withMonitorReturn } from '../routing/monitorContext';
|
||||
import { useMobileLayout } from '../hooks/useMobileLayout';
|
||||
@@ -56,10 +58,11 @@ function VehicleSearch() {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [syncOpen, setSyncOpen] = useState(false);
|
||||
const [candidatesOpen, setCandidatesOpen] = useState(false);
|
||||
const [filtersCollapsed, setFiltersCollapsed] = useState(mobileLayout);
|
||||
const closeTimerRef = useRef<number>();
|
||||
const deferredKeyword = useDeferredValue(keyword.trim());
|
||||
const candidateParams = useMemo(() => {
|
||||
const params = new URLSearchParams({ limit: '8', offset: '0' });
|
||||
const params = new URLSearchParams({ limit: '12', offset: '0' });
|
||||
if (deferredKeyword) params.set('keyword', deferredKeyword);
|
||||
return params;
|
||||
}, [deferredKeyword]);
|
||||
@@ -70,7 +73,7 @@ function VehicleSearch() {
|
||||
gcTime: QUERY_MEMORY.optionGcTime
|
||||
});
|
||||
const options = useMemo(() => mergeVehicleCandidates(candidates.data?.items ?? []), [candidates.data?.items]);
|
||||
const quickVehicles = useMemo(() => options.slice(0, mobileLayout ? 5 : 6), [mobileLayout, options]);
|
||||
const quickVehicles = useMemo(() => options.slice(0, mobileLayout ? 8 : 12), [mobileLayout, options]);
|
||||
useEffect(() => () => window.clearTimeout(closeTimerRef.current), []);
|
||||
const openCandidates = () => {
|
||||
window.clearTimeout(closeTimerRef.current);
|
||||
@@ -94,16 +97,27 @@ function VehicleSearch() {
|
||||
event.preventDefault();
|
||||
openVehicle(keyword);
|
||||
};
|
||||
const vehicleScope = candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权` : '授权范围';
|
||||
return <section className={`v2-vehicle-search-page${syncOpen ? ' has-sync-panel' : ''}${candidatesOpen ? ' has-candidates' : ''}`}>
|
||||
<Card className="v2-vehicle-search-card">
|
||||
<div className="v2-vehicle-search-intro">
|
||||
<span className="v2-search-hero-icon"><IconBox size="extra-large" /></span>
|
||||
<div>
|
||||
<Typography.Title heading={2}>查询单车数字档案</Typography.Title>
|
||||
<Typography.Text type="secondary">通过车牌、VIN 或终端手机号定位车辆,并查看统一身份、实时状态和来源证据。</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
<form className="v2-vehicle-search-form" onSubmit={submit}>
|
||||
<PageHeader
|
||||
title="车辆查询"
|
||||
description="通过车牌、VIN 或终端手机号定位车辆,进入统一数字档案"
|
||||
status={candidates.isPending ? '正在读取授权范围' : vehicleScope}
|
||||
statusColor={candidates.isError ? 'red' : 'blue'}
|
||||
actions={canAdminister(session) ? <Button theme="light" onClick={() => setSyncOpen((value) => !value)}>{syncOpen ? '收起主档同步' : '批量同步主档'}</Button> : null}
|
||||
/>
|
||||
<WorkspaceFilterPanel
|
||||
className="v2-vehicle-query-panel"
|
||||
title="车辆定位"
|
||||
description="车牌优先、VIN 辅助,支持终端手机号精确定位"
|
||||
mobileSummary={deferredKeyword ? `正在查找 ${deferredKeyword}` : candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权车辆` : '正在读取授权范围'}
|
||||
expanded={!filtersCollapsed}
|
||||
status={deferredKeyword ? candidates.isFetching ? '正在查找' : `${options.length} 辆匹配` : vehicleScope}
|
||||
statusColor={deferredKeyword ? 'blue' : 'grey'}
|
||||
collapsedLabel="修改"
|
||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||
>
|
||||
<form className={`v2-vehicle-search-form${filtersCollapsed ? ' is-mobile-collapsed' : ''}`} onSubmit={submit}>
|
||||
<div className={`v2-vehicle-search-picker${candidatesOpen ? ' is-open' : ''}`}>
|
||||
<Input
|
||||
aria-label="搜索车辆"
|
||||
@@ -135,18 +149,13 @@ function VehicleSearch() {
|
||||
onSelect={(vehicle) => openVehicle(vehicle.vin)}
|
||||
/> : null}
|
||||
</form>
|
||||
<div className="v2-vehicle-search-capabilities" aria-label="可查询的数据范围">
|
||||
<span><IconBox /><b>统一身份</b><small>车牌、VIN 与车辆主档</small></span>
|
||||
<span><IconClock /><b>实时状态</b><small>位置、里程与最新上报</small></span>
|
||||
<span><IconTickCircle /><b>来源证据</b><small>协议质量与字段明细</small></span>
|
||||
</div>
|
||||
{canAdminister(session) ? <Button className="v2-profile-sync-open" theme="borderless" onClick={() => setSyncOpen((value) => !value)}>{syncOpen ? '收起批量同步' : '批量同步主档'}</Button> : null}
|
||||
</Card>
|
||||
</WorkspaceFilterPanel>
|
||||
<Card className="v2-vehicle-recent-card" bodyStyle={{ padding: 0 }}>
|
||||
<WorkspacePanelHeader
|
||||
title={deferredKeyword ? '匹配车辆快捷入口' : '最近上报车辆'}
|
||||
description={deferredKeyword ? '按当前搜索条件展示,可直接进入车辆数字档案' : '按照最新上报时间排列,无需搜索即可继续查看'}
|
||||
meta={<Tag color={deferredKeyword ? 'blue' : 'grey'} type="light" size="small">{candidates.data ? `当前显示 ${formatZhNumber(quickVehicles.length)} 辆` : '授权范围'}</Tag>}
|
||||
actions={<Button theme="borderless" type="tertiary" size="small" icon={<IconRefresh />} loading={candidates.isFetching} onClick={() => candidates.refetch()}>刷新</Button>}
|
||||
/>
|
||||
{candidates.isFetching && !candidates.data
|
||||
? <div className="v2-vehicle-recent-state" role="status"><span className="v2-spinner" /><span>正在读取授权车辆…</span></div>
|
||||
|
||||
Reference in New Issue
Block a user