181 lines
7.2 KiB
TypeScript
181 lines
7.2 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
ArrowLeft,
|
|
ChevronRight,
|
|
Sparkles,
|
|
EyeOff,
|
|
CheckCircle2
|
|
} from 'lucide-react';
|
|
import { ViewMode, Step, InventoryMode, Scenario } from '../types';
|
|
import { SetupStep } from './engagement/SetupStep';
|
|
import { InventoryStep } from './engagement/InventoryStep';
|
|
import { ContextStep } from './engagement/ContextStep';
|
|
import { ValueStep } from './engagement/ValueStep';
|
|
import { DeliveryStep } from './engagement/DeliveryStep';
|
|
import { scenarioData as initialScenarioData } from '../data/mockData';
|
|
|
|
interface EngagementViewProps {
|
|
currentStep: Step;
|
|
setCurrentStep: (step: Step) => void;
|
|
setCurrentView: (view: ViewMode) => void;
|
|
isPresentationMode: boolean;
|
|
setIsPresentationMode: (mode: boolean) => void;
|
|
}
|
|
|
|
export const EngagementView: React.FC<EngagementViewProps> = ({
|
|
currentStep,
|
|
setCurrentStep,
|
|
setCurrentView,
|
|
isPresentationMode,
|
|
setIsPresentationMode,
|
|
}) => {
|
|
const [inventoryMode, setInventoryMode] = useState<InventoryMode>('selection');
|
|
// Initialize with scenarios that are marked as selected in mock data
|
|
const [selectedScenarios, setSelectedScenarios] = useState<Scenario[]>(
|
|
initialScenarioData.filter(s => s.selected).map(s => ({ ...s }))
|
|
);
|
|
|
|
// Toggle scenario selection
|
|
const toggleScenarioSelection = (scenarioId: number) => {
|
|
setSelectedScenarios(prev => {
|
|
const isSelected = prev.some(s => s.id === scenarioId);
|
|
if (isSelected) {
|
|
// Remove from selection
|
|
return prev.filter(s => s.id !== scenarioId);
|
|
} else {
|
|
// Add to selection - get full scenario data from initial data
|
|
const scenario = initialScenarioData.find(s => s.id === scenarioId);
|
|
if (!scenario) return prev;
|
|
return [...prev, { ...scenario, selected: true }];
|
|
}
|
|
});
|
|
};
|
|
|
|
// Stepper Configuration
|
|
const steps = [
|
|
{ id: 'setup', label: '1. 项目配置' },
|
|
{ id: 'inventory', label: '2. 数据盘点' },
|
|
{ id: 'context', label: '3. 背景调研' },
|
|
{ id: 'value', label: '4. 价值挖掘' },
|
|
{ id: 'delivery', label: '5. 成果交付' },
|
|
];
|
|
|
|
const currentStepIndex = steps.findIndex(s => s.id === currentStep);
|
|
|
|
return (
|
|
<div className={`bg-slate-50 h-full flex flex-col ${isPresentationMode ? 'p-0' : 'p-6'} overflow-hidden`}>
|
|
{/* Project Header */}
|
|
{!isPresentationMode && (
|
|
<div className="mb-6 flex items-center justify-between">
|
|
<div>
|
|
{/* Back to Project List Navigation */}
|
|
<div className="flex items-center text-sm text-slate-500 mb-1 cursor-pointer hover:text-blue-600 transition-colors" onClick={() => setCurrentView('projects')}>
|
|
<ArrowLeft size={14} className="mr-1" />
|
|
<span>返回项目列表</span>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="flex -space-x-2">
|
|
<div className="w-8 h-8 rounded-full bg-blue-100 border-2 border-white flex items-center justify-center text-xs font-bold text-blue-800" title="Project Manager">PM</div>
|
|
<div className="w-8 h-8 rounded-full bg-purple-100 border-2 border-white flex items-center justify-center text-xs font-bold text-purple-800" title="Data Analyst">DA</div>
|
|
<div className="w-8 h-8 rounded-full bg-gray-100 border-2 border-white flex items-center justify-center text-xs text-gray-500">+2</div>
|
|
</div>
|
|
<div className="h-6 w-px bg-slate-300"></div>
|
|
<button
|
|
onClick={() => setIsPresentationMode(true)}
|
|
className="flex items-center px-4 py-2 bg-slate-900 text-white rounded-md text-sm hover:bg-slate-700 shadow-lg transition-all"
|
|
>
|
|
<Sparkles size={16} className="mr-2" /> 演示模式
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Stepper */}
|
|
<div className={`bg-white border border-slate-200 rounded-lg mb-6 ${isPresentationMode ? 'hidden' : 'block'}`}>
|
|
<div className="flex items-center p-4">
|
|
{steps.map((step, idx) => {
|
|
const isActive = step.id === currentStep;
|
|
const isCompleted = idx < currentStepIndex;
|
|
|
|
return (
|
|
<div key={step.id} className="flex items-center flex-1">
|
|
<div
|
|
onClick={() => {
|
|
setCurrentStep(step.id as Step);
|
|
// Reset inventory mode if returning to that step
|
|
if (step.id === 'inventory' && inventoryMode === 'results') {
|
|
// keep results
|
|
} else if (step.id === 'inventory') {
|
|
setInventoryMode('selection');
|
|
}
|
|
}}
|
|
className={`flex items-center cursor-pointer group ${idx === steps.length - 1 ? 'flex-none' : 'w-full'}`}
|
|
>
|
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold border-2 transition-colors ${
|
|
isActive ? 'border-blue-600 bg-blue-600 text-white' :
|
|
isCompleted ? 'border-green-500 bg-green-500 text-white' : 'border-slate-200 text-slate-400 bg-slate-50'
|
|
}`}>
|
|
{isCompleted ? <CheckCircle2 size={16} /> : idx + 1}
|
|
</div>
|
|
<span className={`ml-3 text-sm font-medium ${isActive ? 'text-slate-900' : 'text-slate-500'}`}>
|
|
{step.label}
|
|
</span>
|
|
{idx !== steps.length - 1 && (
|
|
<div className={`flex-1 h-0.5 mx-4 ${isCompleted ? 'bg-green-500' : 'bg-slate-100'}`}></div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 演示模式退出按钮 */}
|
|
{isPresentationMode && (
|
|
<button
|
|
onClick={() => setIsPresentationMode(false)}
|
|
className="fixed top-4 right-4 z-50 bg-white/90 backdrop-blur text-slate-800 px-4 py-2 rounded-full shadow-lg font-medium text-sm flex items-center hover:bg-white"
|
|
>
|
|
<EyeOff size={16} className="mr-2"/> 退出演示
|
|
</button>
|
|
)}
|
|
|
|
{/* Main Workspace Area */}
|
|
<div className="flex-1 bg-white border border-slate-200 rounded-lg shadow-sm overflow-hidden flex flex-col">
|
|
{currentStep === 'setup' && (
|
|
<SetupStep
|
|
setCurrentStep={setCurrentStep}
|
|
setInventoryMode={setInventoryMode}
|
|
/>
|
|
)}
|
|
|
|
{currentStep === 'inventory' && (
|
|
<InventoryStep
|
|
inventoryMode={inventoryMode}
|
|
setInventoryMode={setInventoryMode}
|
|
setCurrentStep={setCurrentStep}
|
|
/>
|
|
)}
|
|
|
|
{currentStep === 'context' && (
|
|
<ContextStep setCurrentStep={setCurrentStep} />
|
|
)}
|
|
|
|
{currentStep === 'value' && (
|
|
<ValueStep
|
|
setCurrentStep={setCurrentStep}
|
|
selectedScenarios={selectedScenarios}
|
|
allScenarios={initialScenarioData}
|
|
toggleScenarioSelection={toggleScenarioSelection}
|
|
/>
|
|
)}
|
|
|
|
{currentStep === 'delivery' && (
|
|
<DeliveryStep selectedScenarios={selectedScenarios} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|