419 lines
14 KiB
TypeScript
419 lines
14 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||
import { Check, Settings } from 'lucide-react';
|
||
import PageHeader from '@/components/PageHeader';
|
||
import { useToast } from '@/components/ui/toast';
|
||
|
||
// 步骤定义
|
||
const steps = [
|
||
{ id: 1, title: "步骤一", subtitle: "基础设置" },
|
||
{ id: 2, title: "步骤二", subtitle: "好友申请设置" },
|
||
{ id: 3, title: "步骤三", subtitle: "消息设置" },
|
||
];
|
||
|
||
interface ScenarioOption {
|
||
id: string;
|
||
name: string;
|
||
icon: string;
|
||
description: string;
|
||
image: string;
|
||
}
|
||
|
||
const scenarioOptions: ScenarioOption[] = [
|
||
{
|
||
id: "douyin",
|
||
name: "抖音获客",
|
||
icon: "🎵",
|
||
description: "通过抖音平台进行精准获客",
|
||
image: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-QR8ManuDplYTySUJsY4mymiZkDYnQ9.png",
|
||
},
|
||
{
|
||
id: "xiaohongshu",
|
||
name: "小红书获客",
|
||
icon: "📖",
|
||
description: "利用小红书平台进行内容营销获客",
|
||
image: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-yvnMxpoBUzcvEkr8DfvHgPHEo1kmQ3.png",
|
||
},
|
||
{
|
||
id: "gongzhonghao",
|
||
name: "公众号获客",
|
||
icon: "📱",
|
||
description: "通过微信公众号进行获客",
|
||
image: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-Gsg0CMf5tsZb41mioszdjqU1WmsRxW.png",
|
||
},
|
||
{
|
||
id: "haibao",
|
||
name: "海报获客",
|
||
icon: "🖼️",
|
||
description: "通过海报分享进行获客",
|
||
image: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-x92XJgXy4MI7moNYlA1EAes2FqDxMH.png",
|
||
},
|
||
];
|
||
|
||
interface FormData {
|
||
planName: string;
|
||
posters: any[];
|
||
device: any[];
|
||
remarkType: string;
|
||
greeting: string;
|
||
addInterval: number;
|
||
startTime: string;
|
||
endTime: string;
|
||
enabled: boolean;
|
||
sceneId: string;
|
||
scenario: string;
|
||
planNameEdited: boolean;
|
||
}
|
||
|
||
export default function NewPlan() {
|
||
const navigate = useNavigate();
|
||
const searchParams = useSearchParams();
|
||
const { toast } = useToast();
|
||
const [currentStep, setCurrentStep] = useState(1);
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
|
||
const [formData, setFormData] = useState<FormData>({
|
||
planName: "",
|
||
posters: [],
|
||
device: [],
|
||
remarkType: "default",
|
||
greeting: "",
|
||
addInterval: 60,
|
||
startTime: "09:00",
|
||
endTime: "18:00",
|
||
enabled: true,
|
||
sceneId: searchParams[0].get("scenario") || "",
|
||
scenario: searchParams[0].get("scenario") || "",
|
||
planNameEdited: false
|
||
});
|
||
|
||
// 更新表单数据
|
||
const onChange = (data: Partial<FormData>) => {
|
||
if ('planName' in data) {
|
||
setFormData(prev => ({ ...prev, planNameEdited: true, ...data }));
|
||
} else {
|
||
setFormData(prev => ({ ...prev, ...data }));
|
||
}
|
||
};
|
||
|
||
// 处理保存
|
||
const handleSave = async () => {
|
||
try {
|
||
setLoading(true);
|
||
// 模拟API调用
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
|
||
toast({
|
||
title: "创建成功",
|
||
description: "获客计划已创建",
|
||
});
|
||
navigate("/scenarios");
|
||
} catch (error: any) {
|
||
toast({
|
||
title: "创建失败",
|
||
description: error?.message || "创建计划失败,请重试",
|
||
variant: "destructive",
|
||
});
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
// 下一步
|
||
const handleNext = () => {
|
||
if (currentStep === steps.length) {
|
||
handleSave();
|
||
} else {
|
||
setCurrentStep((prev) => prev + 1);
|
||
}
|
||
};
|
||
|
||
// 上一步
|
||
const handlePrev = () => {
|
||
setCurrentStep((prev) => Math.max(prev - 1, 1));
|
||
};
|
||
|
||
// 步骤指示器组件
|
||
const StepIndicator = ({ steps, currentStep }: { steps: any[], currentStep: number }) => (
|
||
<div className="flex items-center justify-between mb-6">
|
||
{steps.map((step, index) => (
|
||
<div key={step.id} className="flex items-center">
|
||
<div className={`flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium ${
|
||
currentStep > step.id
|
||
? 'bg-green-500 text-white'
|
||
: currentStep === step.id
|
||
? 'bg-blue-500 text-white'
|
||
: 'bg-gray-200 text-gray-500'
|
||
}`}>
|
||
{currentStep > step.id ? <Check className="h-4 w-4" /> : step.id}
|
||
</div>
|
||
<div className="ml-2">
|
||
<div className="text-sm font-medium">{step.title}</div>
|
||
<div className="text-xs text-gray-500">{step.subtitle}</div>
|
||
</div>
|
||
{index < steps.length - 1 && (
|
||
<div className={`w-12 h-0.5 mx-4 ${
|
||
currentStep > step.id ? 'bg-green-500' : 'bg-gray-200'
|
||
}`} />
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
|
||
// 基础设置步骤
|
||
const BasicSettings = () => {
|
||
return (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h3 className="text-lg font-medium mb-4">选择获客场景</h3>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
{scenarioOptions.map((scenario) => (
|
||
<div
|
||
key={scenario.id}
|
||
className={`relative p-4 border-2 rounded-lg cursor-pointer transition-all ${
|
||
formData.sceneId === scenario.id
|
||
? 'border-blue-500 bg-blue-50'
|
||
: 'border-gray-200 bg-white hover:border-gray-300'
|
||
}`}
|
||
onClick={() => onChange({ sceneId: scenario.id, scenario: scenario.id })}
|
||
>
|
||
{formData.sceneId === scenario.id && (
|
||
<div className="absolute top-2 right-2 w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
|
||
<Check className="h-4 w-4 text-white" />
|
||
</div>
|
||
)}
|
||
<div className="flex flex-col items-center text-center">
|
||
<img
|
||
src={scenario.image}
|
||
alt={scenario.name}
|
||
className="w-12 h-12 mb-2 rounded"
|
||
/>
|
||
<h3 className="font-medium text-gray-900">{scenario.name}</h3>
|
||
<p className="text-xs text-gray-500 mt-1">{scenario.description}</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-medium">计划信息</h3>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
计划名称 *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={formData.planName}
|
||
onChange={(e) => onChange({ planName: e.target.value })}
|
||
placeholder="请输入计划名称"
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
执行时间
|
||
</label>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-xs text-gray-500 mb-1">开始时间</label>
|
||
<input
|
||
type="time"
|
||
value={formData.startTime}
|
||
onChange={(e) => onChange({ startTime: e.target.value })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-xs text-gray-500 mb-1">结束时间</label>
|
||
<input
|
||
type="time"
|
||
value={formData.endTime}
|
||
onChange={(e) => onChange({ endTime: e.target.value })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4">
|
||
<button
|
||
onClick={handleNext}
|
||
disabled={!formData.sceneId || !formData.planName.trim()}
|
||
className="w-full py-3 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
下一步
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// 好友申请设置步骤
|
||
const FriendRequestSettings = () => (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h3 className="text-lg font-medium mb-4">好友申请设置</h3>
|
||
|
||
<div className="space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
添加间隔(秒)
|
||
</label>
|
||
<input
|
||
type="number"
|
||
value={formData.addInterval}
|
||
onChange={(e) => onChange({ addInterval: parseInt(e.target.value) || 60 })}
|
||
min="30"
|
||
max="3600"
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
<p className="text-xs text-gray-500 mt-1">建议设置60-300秒,避免被限制</p>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
备注类型
|
||
</label>
|
||
<select
|
||
value={formData.remarkType}
|
||
onChange={(e) => onChange({ remarkType: e.target.value })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
>
|
||
<option value="default">默认备注</option>
|
||
<option value="custom">自定义备注</option>
|
||
<option value="none">不添加备注</option>
|
||
</select>
|
||
</div>
|
||
|
||
{formData.remarkType === 'custom' && (
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
自定义备注
|
||
</label>
|
||
<input
|
||
type="text"
|
||
placeholder="请输入自定义备注内容"
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-4 pt-4">
|
||
<button
|
||
onClick={handlePrev}
|
||
className="flex-1 py-3 px-4 border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 transition-colors"
|
||
>
|
||
上一步
|
||
</button>
|
||
<button
|
||
onClick={handleNext}
|
||
className="flex-1 py-3 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
||
>
|
||
下一步
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
// 消息设置步骤
|
||
const MessageSettings = () => (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h3 className="text-lg font-medium mb-4">消息设置</h3>
|
||
|
||
<div className="space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
打招呼消息
|
||
</label>
|
||
<textarea
|
||
value={formData.greeting}
|
||
onChange={(e) => onChange({ greeting: e.target.value })}
|
||
placeholder="请输入打招呼消息内容"
|
||
rows={4}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
|
||
/>
|
||
<p className="text-xs text-gray-500 mt-1">支持变量:{name}(对方昵称)</p>
|
||
</div>
|
||
|
||
<div className="flex items-center">
|
||
<input
|
||
type="checkbox"
|
||
id="enabled"
|
||
checked={formData.enabled}
|
||
onChange={(e) => onChange({ enabled: e.target.checked })}
|
||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||
/>
|
||
<label htmlFor="enabled" className="ml-2 block text-sm text-gray-700">
|
||
立即启用计划
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-4 pt-4">
|
||
<button
|
||
onClick={handlePrev}
|
||
className="flex-1 py-3 px-4 border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 transition-colors"
|
||
>
|
||
上一步
|
||
</button>
|
||
<button
|
||
onClick={handleSave}
|
||
disabled={loading}
|
||
className="flex-1 py-3 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{loading ? '创建中...' : '创建计划'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
// 渲染当前步骤内容
|
||
const renderStepContent = () => {
|
||
switch (currentStep) {
|
||
case 1:
|
||
return <BasicSettings />;
|
||
case 2:
|
||
return <FriendRequestSettings />;
|
||
case 3:
|
||
return <MessageSettings />;
|
||
default:
|
||
return null;
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50">
|
||
<div className="max-w-[390px] mx-auto bg-white min-h-screen flex flex-col">
|
||
<PageHeader
|
||
title="新建获客计划"
|
||
defaultBackPath="/scenarios"
|
||
rightContent={
|
||
<button className="p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
||
<Settings className="h-5 w-5" />
|
||
</button>
|
||
}
|
||
/>
|
||
|
||
{/* 添加pt-16来避免被固定导航栏遮挡 */}
|
||
<div className="flex-1 flex flex-col pt-16">
|
||
<div className="px-4 py-6">
|
||
<StepIndicator steps={steps} currentStep={currentStep} />
|
||
</div>
|
||
|
||
<div className="flex-1 px-4 pb-20">{renderStepContent()}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|