feat(assets): add DeepSeek assistant with streaming results and voice input

This commit is contained in:
shishengliang
2026-09-23 14:16:36 +08:00
parent 822aabf2ca
commit 0c4a9bcd05
14 changed files with 3355 additions and 3 deletions
+7 -2
View File
@@ -1,10 +1,10 @@
import "./library/asset-ui.css";
import "./library/inventory-matrix.css";
import { useCallback, useEffect, useRef, useState } from "react";
import { lazy, Suspense, useCallback, useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { Skeleton } from "@/components/ui/skeleton";
import { ArrowUpRight, Moon, RefreshCw, Sun, Truck } from "lucide-react";
import { ArrowUpRight, Moon, RefreshCw, Sparkles, Sun, Truck } from "lucide-react";
import {
Picker,
Failure,
@@ -43,6 +43,7 @@ import {
CustomerView,
} from "./library/BusinessViews";
import { VehicleDetails } from "./library/VehicleDetails";
const AssetAssistant = lazy(() => import('./assistant/AssetAssistant'));
interface Data {
summary: SummaryData;
@@ -53,6 +54,8 @@ interface Data {
inventory: RegionalInventoryStats[];
}
export default function AssetsModule() {
const [assistantOpen, setAssistantOpen] = useState(false);
const assistantTrigger = useRef<HTMLButtonElement>(null);
const [appearance, setAppearance] = useState<"light" | "dark">(() => {
const saved = window.localStorage.getItem("asset-appearance");
return saved === "light" || saved === "dark"
@@ -202,6 +205,7 @@ export default function AssetsModule() {
/>
</div>
<div className="flex items-center gap-2 self-end">
<Button ref={assistantTrigger} variant="outline" className="min-h-10 gap-2" onClick={() => setAssistantOpen(true)}><Sparkles className="size-4" aria-hidden="true" />AI 问数</Button>
<span className="mr-2 hidden text-xs text-[var(--label-secondary)] sm:block">
{updated
? `更新于 ${updated.slice(11, 16)} · 台账截至 17:00`
@@ -306,6 +310,7 @@ export default function AssetsModule() {
subject={subject}
close={() => setSelection(null)}
/>
{assistantOpen && <Suspense fallback={<div role="status" className="fixed bottom-20 right-6 z-50 rounded-xl bg-white p-4 text-slate-900 shadow-lg">正在打开 AI 问数…</div>}><AssetAssistant key={subject || 'all'} subject={subject} appearance={appearance} close={() => { setAssistantOpen(false); requestAnimationFrame(() => assistantTrigger.current?.focus()); }} /></Suspense>}
</div>
);
}