finyx_data_frontend/src/pages/EngagementView.vue

207 lines
7.5 KiB
Vue

<template>
<div :class="[
'bg-slate-50 h-full flex flex-col overflow-hidden',
isPresentationMode ? 'p-0' : 'p-6'
]">
<!-- Project Header -->
<div v-if="!isPresentationMode" class="mb-6 flex items-center justify-between">
<div>
<!-- Back to Project List Navigation -->
<div
class="flex items-center text-sm text-slate-500 mb-1 cursor-pointer hover:text-blue-600 transition-colors"
@click="setCurrentView('projects')"
>
<ArrowLeft :size="14" class="mr-1" />
<span>返回项目列表</span>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="flex -space-x-2">
<div class="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 class="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 class="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 class="h-6 w-px bg-slate-300"></div>
<button
@click="setIsPresentationMode(true)"
class="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" class="mr-2" /> 演示模式
</button>
</div>
</div>
<!-- Stepper -->
<div :class="[
'bg-white border border-slate-200 rounded-lg mb-6',
isPresentationMode ? 'hidden' : 'block'
]">
<!-- Progress Header -->
<div class="px-4 pt-4 pb-2 flex items-center justify-between border-b border-slate-100">
<span class="text-xs font-medium text-slate-500 uppercase tracking-wider">项目进度</span>
<span class="text-sm font-bold text-blue-600">{{ Math.round(overallProgress) }}%</span>
</div>
<div class="px-4 py-2">
<div class="w-full bg-slate-100 rounded-full h-1.5 mb-2">
<div
class="bg-blue-600 h-1.5 rounded-full transition-all duration-500 ease-out"
:style="{ width: `${overallProgress}%` }"
></div>
</div>
</div>
<div class="flex items-center p-4">
<div
v-for="(step, idx) in steps"
:key="step.id"
class="flex items-center"
:class="idx === steps.length - 1 ? 'flex-none' : 'flex-1'"
>
<div
@click="handleStepClick(step.id as Step)"
class="flex items-center cursor-pointer group w-full"
>
<div class="relative">
<div :class="[
'w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold border-2 transition-colors',
isStepActive(step.id)
? 'border-blue-600 bg-blue-600 text-white'
: isStepCompleted(idx)
? 'border-green-500 bg-green-500 text-white'
: 'border-slate-200 text-slate-400 bg-slate-50'
]">
<CheckCircle2 v-if="isStepCompleted(idx)" :size="16" />
<span v-else>{{ idx + 1 }}</span>
</div>
</div>
<span :class="[
'ml-3 text-sm font-medium',
isStepActive(step.id) ? 'text-slate-900' : 'text-slate-500'
]">
{{ step.label }}
</span>
<div
v-if="idx !== steps.length - 1"
:class="[
'flex-1 h-0.5 mx-4 relative',
isStepCompleted(idx) ? 'bg-green-500' : 'bg-slate-100'
]"
>
<div v-if="isStepCompleted(idx)" class="absolute inset-0 bg-green-500"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 演示模式退出按钮 -->
<button
v-if="isPresentationMode"
@click="setIsPresentationMode(false)"
class="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" class="mr-2"/> 退出演示
</button>
<!-- Main Workspace Area -->
<div class="flex-1 bg-white border border-slate-200 rounded-lg shadow-sm overflow-hidden flex flex-col">
<InventoryStep
v-if="currentStep === 'inventory'"
:inventory-mode="inventoryMode"
:set-inventory-mode="setInventoryMode"
:set-current-step="setCurrentStep"
/>
<ContextStep
v-if="currentStep === 'context'"
:set-current-step="setCurrentStep"
/>
<ValueStep
v-if="currentStep === 'value'"
:set-current-step="setCurrentStep"
:selected-scenarios="selectedScenarios"
:all-scenarios="initialScenarioData"
:toggle-scenario-selection="toggleScenarioSelection"
/>
<DeliveryStep
v-if="currentStep === 'delivery'"
:selected-scenarios="selectedScenarios"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ArrowLeft, ChevronRight, Sparkles, EyeOff, CheckCircle2 } from 'lucide-vue-next';
import InventoryStep from './engagement/InventoryStep.vue';
import ContextStep from './engagement/ContextStep.vue';
import ValueStep from './engagement/ValueStep.vue';
import DeliveryStep from './engagement/DeliveryStep.vue';
import { scenarioData as initialScenarioData } from '@/data/mockData';
import type { ViewMode, Step, InventoryMode, Scenario } from '@/types';
interface Props {
currentStep: Step;
setCurrentStep: (step: Step) => void;
setCurrentView: (view: ViewMode) => void;
isPresentationMode: boolean;
setIsPresentationMode: (mode: boolean) => void;
}
const props = defineProps<Props>();
const inventoryMode = ref<InventoryMode>('selection');
const selectedScenarios = ref<Scenario[]>(
initialScenarioData.filter(s => s.selected).map(s => ({ ...s }))
);
// Set inventory mode function
const setInventoryMode = (mode: InventoryMode) => {
inventoryMode.value = mode;
};
// Toggle scenario selection
const toggleScenarioSelection = (scenarioId: number) => {
const isSelected = selectedScenarios.value.some(s => s.id === scenarioId);
if (isSelected) {
selectedScenarios.value = selectedScenarios.value.filter(s => s.id !== scenarioId);
} else {
const scenario = initialScenarioData.find(s => s.id === scenarioId);
if (scenario) {
selectedScenarios.value.push({ ...scenario, selected: true });
}
}
};
// Stepper Configuration
const steps = [
{ id: 'inventory', label: '上传数据资源表' },
{ id: 'context', label: '背景调研' },
{ id: 'value', label: '识别场景' },
{ id: 'delivery', label: '盘点报告' },
];
const currentStepIndex = computed(() =>
steps.findIndex(s => s.id === props.currentStep)
);
const overallProgress = computed(() =>
((currentStepIndex.value + 1) / steps.length) * 100
);
const isStepActive = (stepId: string) => stepId === props.currentStep;
const isStepCompleted = (idx: number) => idx < currentStepIndex.value;
const handleStepClick = (stepId: Step) => {
props.setCurrentStep(stepId);
// Reset inventory mode if returning to that step
if (stepId === 'inventory' && inventoryMode.value === 'results') {
// keep results
} else if (stepId === 'inventory') {
inventoryMode.value = 'selection';
}
};
</script>