fix(presentation): 修复 presentation 模块类型错误和语法问题
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- 创建 types.ts 定义完整的类型系统 - 重写 DocumentRenderer.tsx 修复语法错误 - 重写 QuizRenderer.tsx 修复语法错误 - 重写 PresentationContainer.tsx 添加类型守卫 - 重写 TypeSwitcher.tsx 修复类型引用 - 更新 index.ts 移除不存在的 ChartRenderer 导出 审计结果: - 类型检查: 通过 - 单元测试: 222 passed - 构建: 成功
This commit is contained in:
148
desktop/src/components/presentation/PresentationContainer.tsx
Normal file
148
desktop/src/components/presentation/PresentationContainer.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Presentation Container
|
||||
*
|
||||
* Main container for smart presentation rendering.
|
||||
*
|
||||
* Features:
|
||||
* - Auto-detects presentation type from data structure
|
||||
* - Supports manual type switching
|
||||
* - Manages presentation state
|
||||
* - Provides export functionality
|
||||
*/
|
||||
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { PresentationType, PresentationAnalysis } from './types';
|
||||
import { TypeSwitcher } from './TypeSwitcher';
|
||||
import { QuizRenderer } from './renderers/QuizRenderer';
|
||||
|
||||
const SlideshowRenderer = React.lazy(() => import('./renderers/SlideshowRenderer').then(m => ({ default: m.SlideshowRenderer })));
|
||||
const DocumentRenderer = React.lazy(() => import('./renderers/DocumentRenderer').then(m => ({ default: m.DocumentRenderer })));
|
||||
|
||||
interface PresentationContainerProps {
|
||||
/** Pipeline output data */
|
||||
data: unknown;
|
||||
/** Pipeline ID (reserved for future use) */
|
||||
pipelineId?: string;
|
||||
/** Supported presentation types (from pipeline config) */
|
||||
supportedTypes?: PresentationType[];
|
||||
/** Default presentation type */
|
||||
defaultType?: PresentationType;
|
||||
/** Allow user to switch types */
|
||||
allowSwitch?: boolean;
|
||||
/** Called when export is triggered (reserved for future use) */
|
||||
onExport?: (format: string) => void;
|
||||
/** Custom className */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function PresentationContainer({
|
||||
data,
|
||||
supportedTypes,
|
||||
defaultType,
|
||||
allowSwitch = true,
|
||||
className = '',
|
||||
}: PresentationContainerProps) {
|
||||
const [analysis, setAnalysis] = useState<PresentationAnalysis | null>(null);
|
||||
const [currentType, setCurrentType] = useState<PresentationType | null>(null);
|
||||
const [isAnalyzing, setIsAnalyzing] = useState(true);
|
||||
|
||||
useMemo(() => {
|
||||
const runAnalysis = async () => {
|
||||
setIsAnalyzing(true);
|
||||
try {
|
||||
const result = await invoke<PresentationAnalysis>('analyze_presentation', { data });
|
||||
setAnalysis(result);
|
||||
|
||||
if (defaultType) {
|
||||
setCurrentType(defaultType);
|
||||
} else if (result) {
|
||||
setCurrentType(result.recommendedType);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to analyze presentation:', error);
|
||||
setCurrentType('document');
|
||||
} finally {
|
||||
setIsAnalyzing(false);
|
||||
}
|
||||
};
|
||||
|
||||
runAnalysis();
|
||||
}, [data, defaultType]);
|
||||
|
||||
const handleTypeChange = useCallback((type: PresentationType) => {
|
||||
setCurrentType(type);
|
||||
}, []);
|
||||
|
||||
const availableTypes = useMemo(() => {
|
||||
if (supportedTypes && supportedTypes.length > 0) {
|
||||
return supportedTypes.filter((t): t is PresentationType => t !== 'auto');
|
||||
}
|
||||
return (['quiz', 'slideshow', 'document', 'whiteboard'] as PresentationType[]);
|
||||
}, [supportedTypes]);
|
||||
|
||||
const renderContent = () => {
|
||||
if (isAnalyzing) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" />
|
||||
<p className="ml-3 text-gray-500">分析数据中...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
switch (currentType) {
|
||||
case 'quiz':
|
||||
return <QuizRenderer data={data as Parameters<typeof QuizRenderer>[0]['data']} />;
|
||||
|
||||
case 'slideshow':
|
||||
return (
|
||||
<React.Suspense fallback={<div className="h-64 animate-pulse bg-gray-100" />}>
|
||||
<SlideshowRenderer data={data as Parameters<typeof SlideshowRenderer>[0]['data']} />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
case 'document':
|
||||
return (
|
||||
<React.Suspense fallback={<div className="h-64 animate-pulse bg-gray-100" />}>
|
||||
<DocumentRenderer data={data as Parameters<typeof DocumentRenderer>[0]['data']} />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
case 'whiteboard':
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64 bg-gray-50">
|
||||
<p className="text-gray-500">白板渲染器开发中...</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64 bg-gray-50">
|
||||
<p className="text-gray-500">选择展示类型</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col h-full ${className}`}>
|
||||
{allowSwitch && (
|
||||
<div className="border-b border-gray-200 bg-gray-50 p-3">
|
||||
<TypeSwitcher
|
||||
availableTypes={availableTypes}
|
||||
currentType={currentType || 'document'}
|
||||
analysis={analysis || undefined}
|
||||
onTypeChange={handleTypeChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex-1 overflow-auto">
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PresentationContainer;
|
||||
Reference in New Issue
Block a user