From 5be6240bbfcd77c9c8f0487d4a8fa7210057ca45 Mon Sep 17 00:00:00 2001 From: shishengliang Date: Wed, 23 Sep 2026 18:35:31 +0800 Subject: [PATCH] feat(asset-ai): simplify count answers and refine mobile conversation layout --- .../assets/assistant/AssetAssistant.tsx | 53 +++++----- .../assets/assistant/CompoundResultView.tsx | 2 +- src/modules/assets/assistant/ResultView.tsx | 19 ++-- .../assets/assistant/ThinkingState.tsx | 30 ++++-- src/modules/assets/assistant/assistant.css | 96 +++++++++++++++++-- .../assets/assistant/concise-count.test.ts | 24 +++++ .../routes/asset-assistant/generative.test.ts | 12 +++ .../routes/asset-assistant/generative.ts | 3 + .../routes/asset-assistant/model.test.ts | 8 ++ src/server/routes/asset-assistant/model.ts | 8 +- .../asset-assistant/presentation.test.ts | 27 ++++++ .../routes/asset-assistant/presentation.ts | 26 +++++ src/shared/assets/assistant.ts | 1 + 13 files changed, 251 insertions(+), 58 deletions(-) create mode 100644 src/modules/assets/assistant/concise-count.test.ts diff --git a/src/modules/assets/assistant/AssetAssistant.tsx b/src/modules/assets/assistant/AssetAssistant.tsx index f94abb5..c13450a 100644 --- a/src/modules/assets/assistant/AssetAssistant.tsx +++ b/src/modules/assets/assistant/AssetAssistant.tsx @@ -30,7 +30,6 @@ import { } from "../../../shared/assets/assistant"; import { askAssetAssistant, AssistantRequestError } from "./client"; import { fetchAuthenticated } from "../../../auth/api-client"; -import { DemandGaps } from "./DemandGaps"; import { ThinkingState } from "./ThinkingState"; import { CompoundResultView } from "./CompoundResultView"; import { BUSINESS_QUESTIONS, conversationTitle, findConversationResult, replaceAnalysisResult } from "./conversation-presentation"; @@ -90,6 +89,23 @@ export default function AssetAssistant({ close: () => void; appearance: "light" | "dark"; }) { + const page = useRef(null); + useLayoutEffect(() => { + const viewport = window.visualViewport; + const updateViewport = () => { + page.current?.style.setProperty('--aq-viewport-height', `${viewport?.height ?? window.innerHeight}px`); + page.current?.style.setProperty('--aq-viewport-top', `${viewport?.offsetTop ?? 0}px`); + }; + updateViewport(); + viewport?.addEventListener('resize', updateViewport); + viewport?.addEventListener('scroll', updateViewport); + window.addEventListener('resize', updateViewport); + return () => { + viewport?.removeEventListener('resize', updateViewport); + viewport?.removeEventListener('scroll', updateViewport); + window.removeEventListener('resize', updateViewport); + }; + }, []); const [theme, setTheme] = useState(appearance); const [turns, setTurns] = useState([]); const turnsRef = useRef(turns); @@ -110,7 +126,6 @@ export default function AssetAssistant({ const [activeResult, setActiveResult] = useState(); const sessionRef = useRef(undefined); const sessions = useRef(new Map()); - const [showGaps, setShowGaps] = useState(false); const nextId = useRef(0); const composer = useRef(null); const scroller = useRef(null); @@ -189,7 +204,6 @@ export default function AssetAssistant({ return () => controller.abort(); }, []); const revealTurn = (id: number) => { - setShowGaps(false); setExpandedTurns((old) => new Set(old).add(id)); autoFollow.current = null; setViewedTurn(id); @@ -232,7 +246,6 @@ export default function AssetAssistant({ action?: AssistantAction, ) => { if (!message.trim() || abortRef.current) return; - setShowGaps(false); setIndependent(false); const inlineTurn = action && base?.resultId ? turnsRef.current.find(turn => !!findConversationResult([turn], base.resultId!)) @@ -344,7 +357,6 @@ export default function AssetAssistant({ ).catch(() => {}); sessionRef.current = undefined; setActiveResult(undefined); - setShowGaps(false); setTurns([]); setExpandedTurns(new Set()); setDraft(""); @@ -355,6 +367,7 @@ export default function AssetAssistant({ }, [subject]); return (
@@ -388,10 +401,6 @@ export default function AssetAssistant({ 返回资产总览 -
本次对话{turns.length.toString().padStart(2, "0")}
@@ -438,13 +447,6 @@ export default function AssetAssistant({ 你的资产分析助手
-
diff --git a/src/modules/assets/assistant/CompoundResultView.tsx b/src/modules/assets/assistant/CompoundResultView.tsx index 4e67c89..42a39c3 100644 --- a/src/modules/assets/assistant/CompoundResultView.tsx +++ b/src/modules/assets/assistant/CompoundResultView.tsx @@ -169,7 +169,7 @@ export function CompoundResultView({ {result.gap && (

{result.gap.recorded - ? "未满足的需求已记录,可在待完善需求中查看。" + ? "未满足的需求已记录。" : "未满足的需求暂未保存。"}

)} diff --git a/src/modules/assets/assistant/ResultView.tsx b/src/modules/assets/assistant/ResultView.tsx index 76cb871..3bc78bb 100644 --- a/src/modules/assets/assistant/ResultView.tsx +++ b/src/modules/assets/assistant/ResultView.tsx @@ -281,7 +281,7 @@ function ResultViewContent({ ); return (
- {plan.kind !== "clarify" && ( + {plan.kind !== "clarify" && !result.conciseCount && (
{result.summary}

- {!!visibleMetrics.length && ( + {!result.conciseCount && !!visibleMetrics.length && (
{visibleMetrics.map((metric) => (
@@ -375,7 +375,7 @@ function ResultViewContent({ ))}
)} - {!!extraMetrics.length && ( + {!result.conciseCount && !!extraMetrics.length && (
查看其余 {extraMetrics.length} 项指标
@@ -405,11 +405,11 @@ function ResultViewContent({ {result.gap && (

{result.gap.recorded - ? "这个需求已记下,可在「待完善需求」中查看。" + ? "这个需求已记下。" : "可以点击下方「未解决我的问题」记录需求。"}

)} - {plan.kind !== "clarify" && ( + {plan.kind !== "clarify" && !result.conciseCount && ( <>
{result.inventoryBreakdown && display !== "metrics" && ( @@ -849,6 +849,11 @@ function ResultViewContent({
)} + {result.conciseCount && result.metrics[0]?.value > 0 && ( + + )}
@@ -873,7 +878,7 @@ function ResultViewContent({

来源:{result.source}

- {plan.kind !== "clarify" && ( + {plan.kind !== "clarify" && (!result.conciseCount || result.metrics[0]?.value > 0) && (
- {!!suggestions.length && ( + {!result.conciseCount && !!suggestions.length && (
{plan.kind === "clarify" ? "你可以这样继续" : "继续分析"} {suggestions.slice(0, 4).map((question) => ( diff --git a/src/modules/assets/assistant/ThinkingState.tsx b/src/modules/assets/assistant/ThinkingState.tsx index c6c7660..bb7e1ef 100644 --- a/src/modules/assets/assistant/ThinkingState.tsx +++ b/src/modules/assets/assistant/ThinkingState.tsx @@ -1,7 +1,7 @@ // Adapted from Beautiful UI's MIT-licensed ThinkingState (Steps). // Uses real server progress rather than the reference's timed demo sequence. -import { useEffect, useState } from "react"; -import { Check, ChevronDown, LoaderCircle, Sparkles } from "lucide-react"; +import { useEffect, useId, useState } from "react"; +import { Check, ChevronDown, CircleAlert, LoaderCircle, Pause, Sparkles } from "lucide-react"; export function ThinkingState({ steps, @@ -18,27 +18,30 @@ export function ThinkingState({ stopped?: boolean; outcome?: string; }) { + const traceId = useId(); const [manualExpanded, setExpanded] = useState(null); const [elapsed, setElapsed] = useState(0); useEffect(() => { if (!working) return; const update = () => setElapsed((Date.now() - started) / 1000); update(); - const timer = window.setInterval(update, 100); + const timer = window.setInterval(update, 1000); return () => window.clearInterval(timer); }, [working, started]); - const expanded = manualExpanded ?? false; + const expanded = manualExpanded ?? working; + const StatusIcon = working ? LoaderCircle : stopped ? Pause : failed ? CircleAlert : Sparkles; return ( -
+
    + {!steps.length &&
  1. {working ? "正在连接分析服务…" : "暂无执行步骤"}
  2. } {steps.map((step, index) => ( -
  3. +
  4. {working && index === steps.length - 1 ? (