feat(asset-ai): generate contextual presentations and drilldown plans
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -55,8 +55,40 @@ import { OverviewView } from './components/OverviewView';
|
||||
const AssetAssistant = lazy(() => import('./assistant/AssetAssistant'));
|
||||
|
||||
export default function AssetsModule() {
|
||||
const [assistantOpen, setAssistantOpen] = useState(false);
|
||||
const [assistantOpen, setAssistantOpen] = useState(() => window.location.hash === '#assets/assistant');
|
||||
const assistantTrigger = useRef<HTMLButtonElement>(null);
|
||||
useEffect(() => {
|
||||
const syncAssistantRoute = () => setAssistantOpen(window.location.hash === '#assets/assistant');
|
||||
window.addEventListener('hashchange', syncAssistantRoute);
|
||||
window.addEventListener('popstate', syncAssistantRoute);
|
||||
return () => {
|
||||
window.removeEventListener('hashchange', syncAssistantRoute);
|
||||
window.removeEventListener('popstate', syncAssistantRoute);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!assistantOpen) return;
|
||||
const previousTitle = document.title;
|
||||
document.title = 'AI 问数 - 资产BI';
|
||||
return () => { document.title = previousTitle; };
|
||||
}, [assistantOpen]);
|
||||
const openAssistant = () => {
|
||||
if (window.location.hash !== '#assets/assistant') {
|
||||
const { pathname, search } = window.location;
|
||||
window.history.pushState({ ...window.history.state, assetAssistantPage: true }, '', `${pathname}${search}#assets/assistant`);
|
||||
}
|
||||
setAssistantOpen(true);
|
||||
};
|
||||
const closeAssistant = () => {
|
||||
if (window.history.state?.assetAssistantPage && window.location.hash === '#assets/assistant') {
|
||||
window.history.back();
|
||||
} else {
|
||||
const { pathname, search } = window.location;
|
||||
window.history.replaceState(window.history.state, '', `${pathname}${search}#assets`);
|
||||
setAssistantOpen(false);
|
||||
}
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => assistantTrigger.current?.focus()));
|
||||
};
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'department' | 'region' | 'customer'>('overview');
|
||||
const [tabReady, setTabReady] = useState(true);
|
||||
const prevTabRef = useRef(activeTab);
|
||||
@@ -431,6 +463,14 @@ export default function AssetsModule() {
|
||||
[customerData, customerChartView, customerProvinceData],
|
||||
);
|
||||
|
||||
if (assistantOpen) {
|
||||
return (
|
||||
<Suspense fallback={<div role="status" className="min-h-screen bg-[var(--app-bg)] p-6 text-slate-700">正在加载 AI 问数页面…</div>}>
|
||||
<AssetAssistant key={selectedSubject || 'all'} subject={selectedSubject} appearance="light" close={closeAssistant} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (loading && !summary) {
|
||||
return (
|
||||
@@ -1710,16 +1750,13 @@ export default function AssetsModule() {
|
||||
<button
|
||||
ref={assistantTrigger}
|
||||
type="button"
|
||||
onClick={() => setAssistantOpen(true)}
|
||||
aria-label="打开 AI 问数"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={assistantOpen}
|
||||
onClick={openAssistant}
|
||||
aria-label="进入 AI 问数页面"
|
||||
className="fixed bottom-[calc(5rem+env(safe-area-inset-bottom))] right-4 z-[60] inline-flex min-h-12 items-center gap-2 rounded-full bg-gradient-to-br from-blue-600 to-cyan-500 px-4 text-sm font-semibold text-white shadow-lg shadow-blue-200 transition hover:from-blue-700 hover:to-cyan-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 md:bottom-6 md:right-6"
|
||||
>
|
||||
<Sparkles size={20} aria-hidden="true" />
|
||||
AI 问数
|
||||
</button>
|
||||
{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={selectedSubject || 'all'} subject={selectedSubject} appearance="light" close={() => { setAssistantOpen(false); requestAnimationFrame(() => assistantTrigger.current?.focus()); }} /></Suspense>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user