From fd514177c6a9228a1e741283098bc18da674de9a Mon Sep 17 00:00:00 2001 From: shishengliang Date: Wed, 23 Sep 2026 18:35:31 +0800 Subject: [PATCH 1/2] ui: remove footer hints and floating feedback entry points --- src/components/Shell.tsx | 9 ++++----- src/modules/assets/AssetsModule.tsx | 3 +-- src/modules/ele/EleImportPage.tsx | 5 +---- src/modules/energy/ETCView.tsx | 10 ---------- src/modules/energy/ElectricDaily.tsx | 3 +-- src/modules/energy/ElectricOverview.tsx | 3 +-- src/modules/energy/HydrogenDaily.tsx | 3 +-- src/modules/energy/HydrogenOverview.tsx | 2 -- src/modules/mileage/MileageModule.tsx | 3 +-- src/modules/scheduling/SchedulingModule.tsx | 3 +-- 10 files changed, 11 insertions(+), 33 deletions(-) diff --git a/src/components/Shell.tsx b/src/components/Shell.tsx index 7777ad9..a72e576 100644 --- a/src/components/Shell.tsx +++ b/src/components/Shell.tsx @@ -3,7 +3,6 @@ import { motion } from 'motion/react'; import { Building2, ChevronRight, ShieldCheck } from 'lucide-react'; import { useAuth } from '../auth/useAuth'; import { DemoModeProvider } from './Blur'; -import FeedbackFab from './FeedbackFab'; import { cn } from '../lib/cn'; import { LoadingState } from './ui/surface'; @@ -91,7 +90,7 @@ export function Shell({ modules }: { modules: ModuleConfig[] }) { return ( -
+
{/* 全局水印 */}
{/* 内容区 */} -
+
羚牛氢能 BI / @@ -165,11 +164,11 @@ export function Shell({ modules }: { modules: ModuleConfig[] }) {
}> {ActiveComponent && } - {activeModule !== 'assets' && } +
{/* 移动端底部导航 (md 以下) */} -
-
); } diff --git a/src/modules/energy/ElectricDaily.tsx b/src/modules/energy/ElectricDaily.tsx index 080a174..b7458e5 100644 --- a/src/modules/energy/ElectricDaily.tsx +++ b/src/modules/energy/ElectricDaily.tsx @@ -4,7 +4,6 @@ import { motion, AnimatePresence } from 'motion/react'; import TrendBadge from './TrendBadge'; import { fetchElectricMonthly } from './api'; import type { CustomerType, DateQuickPick, ElectricMonthGroup } from './types'; -import RotatingFooterHint from '../../components/RotatingFooterHint'; import { EmptyState, ErrorState, LoadingState, MetricTile } from '../../components/ui/surface'; import DailyRangeControls from './daily-range/DailyRangeControls'; import { @@ -186,7 +185,7 @@ export default function ElectricDaily() { })}
)} - +
); } diff --git a/src/modules/energy/ElectricOverview.tsx b/src/modules/energy/ElectricOverview.tsx index db9609a..58c6149 100644 --- a/src/modules/energy/ElectricOverview.tsx +++ b/src/modules/energy/ElectricOverview.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react'; import { BatteryCharging, Gauge, Wallet, CalendarClock } from 'lucide-react'; import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, Tooltip, ReferenceLine } from 'recharts'; import { fetchElectricOverview, type ElectricOverviewResponse } from './api'; -import RotatingFooterHint from '../../components/RotatingFooterHint'; import { ErrorState, LoadingState, MetricTile, SurfaceCard } from '../../components/ui/surface'; function fmtYuan(yuan: number) { @@ -122,7 +121,7 @@ export default function ElectricOverview() { - + ); } diff --git a/src/modules/energy/HydrogenDaily.tsx b/src/modules/energy/HydrogenDaily.tsx index d162d10..3f6b4b1 100644 --- a/src/modules/energy/HydrogenDaily.tsx +++ b/src/modules/energy/HydrogenDaily.tsx @@ -13,7 +13,6 @@ import { type RangeMode, } from './hydrogen-daily/model'; import DailyRangeControls from './daily-range/DailyRangeControls'; -import RotatingFooterHint from '../../components/RotatingFooterHint'; import { EmptyState, ErrorState, LoadingState, MetricTile, SurfaceCard } from '../../components/ui/surface'; export default function HydrogenDaily() { @@ -272,7 +271,7 @@ export default function HydrogenDaily() { })} )} - + ); } diff --git a/src/modules/energy/HydrogenOverview.tsx b/src/modules/energy/HydrogenOverview.tsx index add9f0f..ea80385 100644 --- a/src/modules/energy/HydrogenOverview.tsx +++ b/src/modules/energy/HydrogenOverview.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import RotatingFooterHint from '../../components/RotatingFooterHint'; import { fetchHydrogenOverview, type HydrogenOverviewResponse } from './api'; import { DistributionCharts } from './hydrogen-overview/components/DistributionCharts'; import { HydrogenOverviewSkeleton } from './hydrogen-overview/components/HydrogenOverviewSkeleton'; @@ -99,7 +98,6 @@ export default function HydrogenOverview() { - ); diff --git a/src/modules/mileage/MileageModule.tsx b/src/modules/mileage/MileageModule.tsx index 9466e5f..9bd6005 100644 --- a/src/modules/mileage/MileageModule.tsx +++ b/src/modules/mileage/MileageModule.tsx @@ -4,7 +4,6 @@ import MonitoringView from './MonitoringView'; import StatisticsView from './StatisticsView'; import DailyReportView from './DailyReportView'; import { useHashSubTab } from '../energy/useHashSubTab'; -import RotatingFooterHint from '../../components/RotatingFooterHint'; import { FadeIn, PageFrame, SegmentedNav } from '../../components/ui/surface'; type MileageSubTab = 'monitoring' | 'statistics' | 'report'; @@ -45,7 +44,7 @@ export default function MileageModule() { )} - + ); } diff --git a/src/modules/scheduling/SchedulingModule.tsx b/src/modules/scheduling/SchedulingModule.tsx index 9dc88a8..a88eaa4 100644 --- a/src/modules/scheduling/SchedulingModule.tsx +++ b/src/modules/scheduling/SchedulingModule.tsx @@ -7,7 +7,6 @@ import SuggestionList from './SuggestionList'; import SuggestionDetail from './SuggestionDetail'; import NotificationHistory from './NotificationHistory'; import { exportSuggestionsCsv } from './csv-export'; -import RotatingFooterHint from '../../components/RotatingFooterHint'; import { PageFrame } from '../../components/ui/surface'; import ActiveFilterTags from './scheduling-module/ActiveFilterTags'; import BatchActionBar from './scheduling-module/BatchActionBar'; @@ -309,7 +308,7 @@ export default function SchedulingModule() { onSubmit={handleBatchSubmit} /> )} - + ); } From 5be6240bbfcd77c9c8f0487d4a8fa7210057ca45 Mon Sep 17 00:00:00 2001 From: shishengliang Date: Wed, 23 Sep 2026 18:35:31 +0800 Subject: [PATCH 2/2] 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 ? (