feat: 本次提交更新内容如下
定版本转移2025年7月17日
This commit is contained in:
@@ -1,239 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { ArrowLeft, Users, TrendingUp, Calendar, Settings, Play, Pause, Edit } from 'lucide-react';
|
||||
|
||||
interface PlanData {
|
||||
id: string;
|
||||
name: string;
|
||||
status: 'active' | 'paused' | 'completed';
|
||||
createdAt: string;
|
||||
totalCustomers: number;
|
||||
todayCustomers: number;
|
||||
growth: string;
|
||||
description?: string;
|
||||
scenario: string;
|
||||
}
|
||||
|
||||
export default function PlanDetail() {
|
||||
const { planId } = useParams<{ planId: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [plan, setPlan] = useState<PlanData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPlanData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API调用
|
||||
const mockPlan: PlanData = {
|
||||
id: planId || '',
|
||||
name: '春季营销计划',
|
||||
status: 'active',
|
||||
createdAt: '2024-03-15',
|
||||
totalCustomers: 456,
|
||||
todayCustomers: 23,
|
||||
growth: '+8.2%',
|
||||
description: '针对春季市场的营销推广计划,通过多种渠道获取潜在客户',
|
||||
scenario: 'douyin',
|
||||
};
|
||||
|
||||
setPlan(mockPlan);
|
||||
} catch (error) {
|
||||
setError('获取计划数据失败');
|
||||
console.error('获取计划数据失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPlanData();
|
||||
}, [planId]);
|
||||
|
||||
const handleStatusChange = async (newStatus: 'active' | 'paused') => {
|
||||
if (!plan) return;
|
||||
|
||||
try {
|
||||
// 这里可以调用实际的API
|
||||
// await fetch(`/api/plans/${plan.id}/status`, {
|
||||
// method: 'PATCH',
|
||||
// headers: { 'Content-Type': 'application/json' },
|
||||
// body: JSON.stringify({ status: newStatus }),
|
||||
// });
|
||||
|
||||
setPlan({ ...plan, status: newStatus });
|
||||
} catch (error) {
|
||||
console.error('更新计划状态失败:', error);
|
||||
alert('更新失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-20 bg-gray-50">
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<div className="text-gray-500">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !plan) {
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-20 bg-gray-50">
|
||||
<div className="text-red-500 text-center py-8">{error || '计划不存在'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-20 bg-gray-50">
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
className="mr-3 p-1 hover:bg-gray-100 rounded"
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</button>
|
||||
<h1 className="text-xl font-semibold">{plan.name}</h1>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={() => navigate(`/plans/${plan.id}/edit`)}
|
||||
className="p-2 hover:bg-gray-100 rounded"
|
||||
>
|
||||
<Edit className="h-5 w-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigate(`/plans/${plan.id}/settings`)}
|
||||
className="p-2 hover:bg-gray-100 rounded"
|
||||
>
|
||||
<Settings className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="p-4">
|
||||
{/* 计划描述 */}
|
||||
{plan.description && (
|
||||
<div className="bg-white rounded-lg p-4 mb-6">
|
||||
<p className="text-gray-600 text-sm">{plan.description}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 数据统计 */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="bg-white rounded-lg p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">总获客数</p>
|
||||
<p className="text-2xl font-bold text-gray-900">{plan.totalCustomers}</p>
|
||||
</div>
|
||||
<Users className="h-8 w-8 text-blue-500" />
|
||||
</div>
|
||||
<div className="flex items-center mt-2 text-green-500 text-sm">
|
||||
<TrendingUp className="h-4 w-4 mr-1" />
|
||||
<span>{plan.growth}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">今日获客</p>
|
||||
<p className="text-2xl font-bold text-gray-900">{plan.todayCustomers}</p>
|
||||
</div>
|
||||
<Calendar className="h-8 w-8 text-green-500" />
|
||||
</div>
|
||||
<div className="flex items-center mt-2 text-gray-500 text-sm">
|
||||
<span>创建于 {plan.createdAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 状态控制 */}
|
||||
<div className="bg-white rounded-lg p-4 mb-6">
|
||||
<h3 className="text-lg font-medium mb-4">计划状态</h3>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<span className={`px-3 py-1 rounded-full text-sm ${
|
||||
plan.status === 'active'
|
||||
? 'text-green-600 bg-green-50'
|
||||
: plan.status === 'paused'
|
||||
? 'text-yellow-600 bg-yellow-50'
|
||||
: 'text-gray-600 bg-gray-50'
|
||||
}`}>
|
||||
{plan.status === 'active' ? '进行中' : plan.status === 'paused' ? '已暂停' : '已完成'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
{plan.status === 'active' ? (
|
||||
<button
|
||||
onClick={() => handleStatusChange('paused')}
|
||||
className="flex items-center px-3 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-600 transition-colors text-sm"
|
||||
>
|
||||
<Pause className="h-4 w-4 mr-1" />
|
||||
暂停
|
||||
</button>
|
||||
) : plan.status === 'paused' ? (
|
||||
<button
|
||||
onClick={() => handleStatusChange('active')}
|
||||
className="flex items-center px-3 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 transition-colors text-sm"
|
||||
>
|
||||
<Play className="h-4 w-4 mr-1" />
|
||||
启动
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 功能区域 */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="bg-white rounded-lg p-4 cursor-pointer hover:shadow-md transition-shadow" onClick={() => navigate(`/plans/${plan.id}/customers`)}>
|
||||
<div className="flex items-center">
|
||||
<Users className="h-8 w-8 text-blue-500 mr-3" />
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900">客户管理</h3>
|
||||
<p className="text-sm text-gray-500">查看和管理获客客户</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-4 cursor-pointer hover:shadow-md transition-shadow" onClick={() => navigate(`/plans/${plan.id}/analytics`)}>
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="h-8 w-8 text-green-500 mr-3" />
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900">数据分析</h3>
|
||||
<p className="text-sm text-gray-500">查看获客数据统计</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-4 cursor-pointer hover:shadow-md transition-shadow" onClick={() => navigate(`/plans/${plan.id}/content`)}>
|
||||
<div className="flex items-center">
|
||||
<Calendar className="h-8 w-8 text-purple-500 mr-3" />
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900">内容管理</h3>
|
||||
<p className="text-sm text-gray-500">管理营销内容</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-4 cursor-pointer hover:shadow-md transition-shadow" onClick={() => navigate(`/plans/${plan.id}/settings`)}>
|
||||
<div className="flex items-center">
|
||||
<Settings className="h-8 w-8 text-gray-500 mr-3" />
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900">计划设置</h3>
|
||||
<p className="text-sm text-gray-500">配置计划参数</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Plus, Calendar } from 'lucide-react';
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
|
||||
interface Plan {
|
||||
id: string;
|
||||
name: string;
|
||||
status: 'active' | 'paused' | 'completed';
|
||||
createdAt: string;
|
||||
totalCustomers: number;
|
||||
todayCustomers: number;
|
||||
growth: string;
|
||||
scenario: string;
|
||||
}
|
||||
|
||||
export default function Plans() {
|
||||
const navigate = useNavigate();
|
||||
const [plans, setPlans] = useState<Plan[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPlans = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API调用
|
||||
const mockPlans: Plan[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: '春季营销计划',
|
||||
status: 'active',
|
||||
createdAt: '2024-03-15',
|
||||
totalCustomers: 456,
|
||||
todayCustomers: 23,
|
||||
growth: '+8.2%',
|
||||
scenario: 'douyin',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '新品推广计划',
|
||||
status: 'active',
|
||||
createdAt: '2024-03-10',
|
||||
totalCustomers: 234,
|
||||
todayCustomers: 15,
|
||||
growth: '+5.1%',
|
||||
scenario: 'xiaohongshu',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '节日活动计划',
|
||||
status: 'paused',
|
||||
createdAt: '2024-02-28',
|
||||
totalCustomers: 789,
|
||||
todayCustomers: 0,
|
||||
growth: '+0%',
|
||||
scenario: 'gongzhonghao',
|
||||
},
|
||||
];
|
||||
|
||||
setPlans(mockPlans);
|
||||
} catch (error) {
|
||||
setError('获取计划数据失败');
|
||||
console.error('获取计划数据失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPlans();
|
||||
}, []);
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return 'text-green-600 bg-green-50';
|
||||
case 'paused':
|
||||
return 'text-yellow-600 bg-yellow-50';
|
||||
case 'completed':
|
||||
return 'text-gray-600 bg-gray-50';
|
||||
default:
|
||||
return 'text-gray-600 bg-gray-50';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return '进行中';
|
||||
case 'paused':
|
||||
return '已暂停';
|
||||
case 'completed':
|
||||
return '已完成';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-20 bg-gray-50">
|
||||
<PageHeader
|
||||
title="获客计划"
|
||||
showBack={false}
|
||||
/>
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<div className="text-gray-500">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-20 bg-gray-50">
|
||||
<PageHeader
|
||||
title="获客计划"
|
||||
showBack={false}
|
||||
/>
|
||||
<div className="text-red-500 text-center py-8">{error}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-20 bg-gray-50">
|
||||
<PageHeader
|
||||
title="获客计划"
|
||||
showBack={false}
|
||||
rightContent={
|
||||
<button
|
||||
onClick={() => navigate('/scenarios/new')}
|
||||
className="flex items-center px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
新建计划
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="p-4">
|
||||
{plans.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
<p>暂无获客计划</p>
|
||||
<button
|
||||
onClick={() => navigate('/scenarios/new')}
|
||||
className="mt-2 text-blue-600 hover:text-blue-700"
|
||||
>
|
||||
立即创建
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{plans.map((plan) => (
|
||||
<div
|
||||
key={plan.id}
|
||||
className="bg-white rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer"
|
||||
onClick={() => navigate(`/plans/${plan.id}`)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center mb-2">
|
||||
<h3 className="font-medium text-gray-900">{plan.name}</h3>
|
||||
<span className={`ml-2 px-2 py-1 text-xs rounded-full ${getStatusColor(plan.status)}`}>
|
||||
{getStatusText(plan.status)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<Calendar className="h-4 w-4 mr-1" />
|
||||
<span>创建于 {plan.createdAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right">
|
||||
<div className="flex items-center text-sm">
|
||||
<span className="text-gray-500">总获客:</span>
|
||||
<span className="font-medium ml-1">{plan.totalCustomers}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm mt-1">
|
||||
<span className="text-gray-500">今日:</span>
|
||||
<span className="font-medium ml-1">{plan.todayCustomers}</span>
|
||||
<span className="text-green-500 ml-1">({plan.growth})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user