refine Semi UI vehicle directory

This commit is contained in:
lingniu
2026-07-18 13:29:46 +08:00
parent c7123f6fbd
commit 3b06dd86a8
3 changed files with 61 additions and 11 deletions

View File

@@ -4,12 +4,12 @@ import { Button, Card, Empty, Input, Table, Tag } from '@douyinfe/semi-ui';
import { type FormEvent, lazy, Suspense, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { api } from '../../api/client';
import type { VehicleCoverageRow } from '../../api/types';
import type { Page, VehicleCoverageRow } from '../../api/types';
import { usePlatformSession } from '../auth/AuthGate';
import { canAdminister } from '../auth/session';
import { formatZhNumber } from '../domain/formatters';
import { useMobileLayout } from '../hooks/useMobileLayout';
import { QUERY_MEMORY } from '../queryPolicy';
import { QUERY_MEMORY, queryScopeKey, retainPreviousPageWithinScope } from '../queryPolicy';
import { TablePagination } from '../shared/TablePagination';
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
@@ -54,15 +54,16 @@ export default function VehicleSearchWorkspace() {
const closeTimerRef = useRef<number>();
const deferredKeyword = useDeferredValue(keyword.trim());
const pageSize = mobileLayout ? 8 : 10;
const directoryScope = useMemo(() => queryScopeKey({ keyword: deferredKeyword }), [deferredKeyword]);
const candidateParams = useMemo(() => {
const params = new URLSearchParams({ limit: String(pageSize), offset: String((page - 1) * pageSize) });
if (deferredKeyword) params.set('keyword', deferredKeyword);
return params;
}, [deferredKeyword, page, pageSize]);
const candidates = useQuery({
queryKey: ['vehicle-directory', candidateParams.toString()],
const candidates = useQuery<Page<VehicleCoverageRow>>({
queryKey: ['vehicle-directory', directoryScope, pageSize, page],
queryFn: ({ signal }) => api.vehicleCoverage(candidateParams, signal),
placeholderData: (previous) => previous,
placeholderData: retainPreviousPageWithinScope<Page<VehicleCoverageRow>>(directoryScope),
staleTime: 30_000,
gcTime: QUERY_MEMORY.optionGcTime
});
@@ -80,7 +81,7 @@ export default function VehicleSearchWorkspace() {
}, [mobileLayout]);
const openCandidates = () => {
window.clearTimeout(closeTimerRef.current);
setCandidatesOpen(true);
setCandidatesOpen(Boolean(keyword.trim()));
};
const closeCandidates = () => {
window.clearTimeout(closeTimerRef.current);
@@ -99,7 +100,8 @@ export default function VehicleSearchWorkspace() {
};
const submit = (event: FormEvent) => {
event.preventDefault();
openVehicle(keyword);
setCandidatesOpen(false);
if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
};
const vehicleScope = candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权` : '授权范围';
return <section className={`v2-vehicle-search-page${syncOpen ? ' has-sync-panel' : ''}${candidatesOpen ? ' has-candidates' : ''}`}>
@@ -132,7 +134,7 @@ export default function VehicleSearchWorkspace() {
aria-expanded={candidatesOpen}
prefix={<IconSearch />}
value={keyword}
onChange={(value) => { setKeyword(value); setPage(1); setCandidatesOpen(true); }}
onChange={(value) => { setKeyword(value); setPage(1); setCandidatesOpen(Boolean(value.trim())); }}
onFocus={openCandidates}
onBlur={closeCandidates}
placeholder="输入车牌 / VIN / 终端手机号"
@@ -140,7 +142,7 @@ export default function VehicleSearchWorkspace() {
/>
</div>
<Button theme="solid" htmlType="submit" icon={<IconArrowRight />} iconPosition="right"></Button>
{candidatesOpen ? <VehicleCandidateList
{candidatesOpen && deferredKeyword ? <VehicleCandidateList
id="v2-vehicle-search-options"
className="v2-vehicle-search-options"
items={options}