import { useMemo, useState } from 'react'; import { trpc } from '@/lib/trpc'; import { ChevronRight, ChevronDown, Plus, Trash2, Train, Box, Check, X, } from 'lucide-react'; export type TreeSelection = { seriesId: number | null; modelId: number | null; taskId: number | null; }; interface Props { selection: TreeSelection; onSelect: (s: TreeSelection) => void; } function EditableText({ value, onSave, }: { value: string; onSave: (v: string) => void; }) { const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(value); if (!editing) { return ( { setDraft(value); setEditing(true); }} > {value} ); } return ( setDraft(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') { onSave(draft); setEditing(false); } if (e.key === 'Escape') setEditing(false); }} /> ); } export default function VehicleTree({ selection, onSelect }: Props) { const utils = trpc.useUtils(); const [expandedSeries, setExpandedSeries] = useState>(new Set()); const [newSeriesName, setNewSeriesName] = useState(''); const [addingSeries, setAddingSeries] = useState(false); const [addingModelFor, setAddingModelFor] = useState(null); const [newModelName, setNewModelName] = useState(''); const { data: seriesList = [] } = trpc.vehicleSeries.list.useQuery(); // 固定次数的 Hooks:用 trpc.useQueries(内部是单个 useQueries),禁止在 map 里调 useQuery const modelQueryResults = trpc.useQueries((t) => seriesList.map((s) => t.vehicleModel.listBySeries( { seriesId: s.id }, { enabled: expandedSeries.has(s.id) } ) ) ); const createSeries = trpc.vehicleSeries.create.useMutation({ onSuccess: () => utils.vehicleSeries.list.invalidate(), }); const deleteSeries = trpc.vehicleSeries.delete.useMutation({ onSuccess: () => utils.vehicleSeries.list.invalidate(), }); const updateSeries = trpc.vehicleSeries.update.useMutation({ onSuccess: () => utils.vehicleSeries.list.invalidate(), }); const createModel = trpc.vehicleModel.create.useMutation({ onSuccess: (_, vars) => utils.vehicleModel.listBySeries.invalidate({ seriesId: vars.seriesId }), }); const deleteModel = trpc.vehicleModel.delete.useMutation({ onSuccess: () => { seriesList.forEach(s => utils.vehicleModel.listBySeries.invalidate({ seriesId: s.id })); }, }); const updateModel = trpc.vehicleModel.update.useMutation({ onSuccess: () => { seriesList.forEach(s => utils.vehicleModel.listBySeries.invalidate({ seriesId: s.id })); }, }); function toggleSeries(id: number) { setExpandedSeries(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); } return (
车系 / 车型 / 任务
{addingSeries && (
setNewSeriesName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter' && newSeriesName.trim()) { createSeries.mutate({ name: newSeriesName.trim() }); setNewSeriesName(''); setAddingSeries(false); } if (e.key === 'Escape') setAddingSeries(false); }} />
)} {seriesList.map((series, si) => { const expanded = expandedSeries.has(series.id); const models = modelQueryResults[si]?.data ?? []; const isSeriesSelected = selection.seriesId === series.id && !selection.modelId; return (
{/* 车系行 */}
{ toggleSeries(series.id); onSelect({ seriesId: series.id, modelId: null, taskId: null }); }} > {expanded ? : } updateSeries.mutate({ id: series.id, name })} />
{/* 新增车型输入 */} {addingModelFor === series.id && (
setNewModelName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter' && newModelName.trim()) { createModel.mutate({ seriesId: series.id, name: newModelName.trim() }); setNewModelName(''); setAddingModelFor(null); setExpandedSeries(prev => { const n = new Set(prev); n.add(series.id); return n; }); } if (e.key === 'Escape') setAddingModelFor(null); }} />
)} {/* 车型列表 */} {expanded && models.map(model => { const isModelSelected = selection.modelId === model.id; return (
onSelect({ seriesId: series.id, modelId: model.id, taskId: null })} > updateModel.mutate({ id: model.id, name })} />
); })}
); })} {seriesList.length === 0 && (
暂无车系,点击右上角新增
)}
); }