feat: 保存了

This commit is contained in:
许永平
2025-07-09 20:07:12 +08:00
parent cc35ccf1d8
commit 6d417dae2a
6 changed files with 639 additions and 114 deletions

View File

@@ -8,6 +8,8 @@ import { createMomentsSyncTask } from '@/api/momentsSync';
import { ChevronLeft, Clock, Plus, Minus, Search } from 'lucide-react';
import Layout from '@/components/Layout';
import BottomNav from '@/components/BottomNav';
import { DeviceSelectionDialog } from '@/components/DeviceSelectionDialog';
import { ContentLibrarySelectionDialog } from '@/components/ContentLibrarySelectionDialog';
// 步骤指示器组件
interface StepIndicatorProps {
@@ -23,6 +25,17 @@ function StepIndicator({ currentStep }: StepIndicatorProps) {
return (
<div className="relative flex justify-between px-6">
{/* 背景连线 */}
<div className="absolute top-4 left-6 right-6 h-[2px] bg-gray-200">
<div
className="absolute top-0 left-0 h-full bg-blue-600 transition-all duration-300 ease-in-out"
style={{
width: currentStep > 1 ? `${((currentStep - 1) / (steps.length - 1)) * 100}%` : '0%'
}}
/>
</div>
{/* 步骤圆圈 */}
{steps.map((step) => (
<div
key={step.id}
@@ -31,10 +44,10 @@ function StepIndicator({ currentStep }: StepIndicatorProps) {
}`}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center transition-all ${
className={`w-8 h-8 rounded-full flex items-center justify-center transition-all duration-300 ${
currentStep >= step.id
? "bg-blue-600 text-white shadow-sm"
: "bg-white border border-gray-200 text-gray-400"
: "bg-white border-2 border-gray-200 text-gray-400"
}`}
>
{step.id}
@@ -42,12 +55,6 @@ function StepIndicator({ currentStep }: StepIndicatorProps) {
<div className="text-xs mt-2 font-medium">{step.subtitle}</div>
</div>
))}
<div className="absolute top-4 left-0 right-0 h-[1px] bg-gray-100 -z-10">
<div
className="absolute top-0 left-0 h-full bg-blue-600 transition-all duration-300"
style={{ width: `${((currentStep - 1) / (steps.length - 1)) * 100}%` }}
/>
</div>
</div>
);
}
@@ -59,8 +66,12 @@ interface BasicSettingsProps {
startTime: string;
endTime: string;
syncCount: number;
interval: number;
accountType: "business" | "personal";
enabled: boolean;
contentTypes: ('text' | 'image' | 'video')[];
targetTags: string[];
filterKeywords: string[];
};
onChange: (data: Partial<BasicSettingsProps["formData"]>) => void;
onNext: () => void;
@@ -112,23 +123,47 @@ function BasicSettings({ formData, onChange, onNext }: BasicSettingsProps) {
variant="outline"
size="lg"
onClick={() => onChange({ syncCount: Math.max(1, formData.syncCount - 1) })}
className="h-12 w-12 rounded-xl bg-white border-gray-200"
className="h-12 w-12 rounded-xl bg-white border-gray-200 text-xl font-bold"
>
<Minus className="h-5 w-5" />
-
</Button>
<span className="w-8 text-center text-lg font-medium">{formData.syncCount}</span>
<Button
variant="outline"
size="lg"
onClick={() => onChange({ syncCount: formData.syncCount + 1 })}
className="h-12 w-12 rounded-xl bg-white border-gray-200"
className="h-12 w-12 rounded-xl bg-white border-gray-200 text-xl font-bold"
>
<Plus className="h-5 w-5" />
+
</Button>
<span className="text-gray-500"></span>
</div>
</div>
<div>
<div className="text-base font-medium mb-2"></div>
<div className="flex items-center space-x-5">
<Button
variant="outline"
size="lg"
onClick={() => onChange({ interval: Math.max(1, formData.interval - 1) })}
className="h-12 w-12 rounded-xl bg-white border-gray-200 text-xl font-bold"
>
-
</Button>
<span className="w-8 text-center text-lg font-medium">{formData.interval}</span>
<Button
variant="outline"
size="lg"
onClick={() => onChange({ interval: formData.interval + 1 })}
className="h-12 w-12 rounded-xl bg-white border-gray-200 text-xl font-bold"
>
+
</Button>
<span className="text-gray-500"></span>
</div>
</div>
<div>
<div className="text-base font-medium mb-2"></div>
<div className="flex space-x-4">
@@ -161,6 +196,32 @@ function BasicSettings({ formData, onChange, onNext }: BasicSettingsProps) {
</div>
</div>
<div>
<div className="text-base font-medium mb-2"></div>
<div className="flex space-x-4">
{(['text', 'image', 'video'] as const).map((type) => (
<div key={type} className="flex-1">
<Button
variant="ghost"
onClick={() => {
const newTypes = formData.contentTypes.includes(type)
? formData.contentTypes.filter(t => t !== type)
: [...formData.contentTypes, type];
onChange({ contentTypes: newTypes });
}}
className={`w-full h-12 justify-between rounded-lg ${
formData.contentTypes.includes(type)
? "bg-blue-600 hover:bg-blue-600 text-white"
: "bg-white hover:bg-gray-50"
}`}
>
{type === 'text' ? '文字' : type === 'image' ? '图片' : '视频'}
</Button>
</div>
))}
</div>
</div>
<div className="flex items-center justify-between py-2">
<span className="text-base font-medium"></span>
<Switch
@@ -186,15 +247,21 @@ export default function NewMomentsSyncTask() {
const { toast } = useToast();
const [currentStep, setCurrentStep] = useState(1);
const [loading, setLoading] = useState(false);
const [deviceDialogOpen, setDeviceDialogOpen] = useState(false);
const [contentLibraryDialogOpen, setContentLibraryDialogOpen] = useState(false);
const [formData, setFormData] = useState({
taskName: '',
startTime: '06:00',
endTime: '23:59',
syncCount: 5,
interval: 30, // 时间间隔,单位:分钟
accountType: 'business' as 'business' | 'personal',
enabled: true,
selectedDevices: [] as string[],
selectedLibraries: [] as string[],
contentTypes: ['text', 'image', 'video'] as ('text' | 'image' | 'video')[],
targetTags: [] as string[],
filterKeywords: [] as string[],
});
const handleUpdateFormData = (data: Partial<typeof formData>) => {
@@ -230,13 +297,13 @@ export default function NewMomentsSyncTask() {
devices: formData.selectedDevices,
contentLib: formData.selectedLibraries.join(','),
syncMode: formData.accountType === 'business' ? 'auto' : 'manual',
interval: 30,
interval: formData.interval,
maxSync: formData.syncCount,
startTime: formData.startTime,
endTime: formData.endTime,
targetTags: [],
contentTypes: ['text', 'image', 'video'],
filterKeywords: [],
targetTags: formData.targetTags,
contentTypes: formData.contentTypes,
filterKeywords: formData.filterKeywords,
friends: [],
});
toast({ title: '创建成功' });
@@ -272,72 +339,100 @@ export default function NewMomentsSyncTask() {
<BasicSettings formData={formData} onChange={handleUpdateFormData} onNext={handleNext} />
)}
{currentStep === 2 && (
<div className="space-y-6">
<div className="relative">
<Search className="absolute left-3 top-4 h-5 w-5 text-gray-400" />
<Input
placeholder="选择设备"
className="h-12 pl-11 rounded-xl border-gray-200 text-base"
onClick={() => {
// TODO: 打开设备选择弹窗
toast({ title: '设备选择功能开发中' });
}}
readOnly
/>
</div>
{formData.selectedDevices.length > 0 && (
<div className="text-base text-gray-500">{formData.selectedDevices.length} </div>
)}
<div className="flex space-x-4 pt-4">
<Button variant="outline" onClick={handlePrev} className="flex-1 h-12 rounded-xl text-base">
</Button>
<Button
onClick={handleNext}
className="flex-1 h-12 bg-blue-600 hover:bg-blue-700 rounded-xl text-base shadow-sm"
>
</Button>
</div>
{currentStep === 2 && (
<div className="space-y-6">
<div className="relative">
<Search className="absolute left-3 top-4 h-5 w-5 text-gray-400" />
<Input
placeholder="选择设备"
className="h-12 pl-11 rounded-xl border-gray-200 text-base"
onClick={() => setDeviceDialogOpen(true)}
readOnly
/>
</div>
)}
{currentStep === 3 && (
<div className="space-y-6">
<div className="relative">
<Search className="absolute left-3 top-4 h-5 w-5 text-gray-400" />
<Input
placeholder="选择内容库"
className="h-12 pl-11 rounded-xl border-gray-200 text-base"
onClick={() => {
// TODO: 打开内容库选择弹窗
toast({ title: '内容库选择功能开发中' });
}}
readOnly
/>
{formData.selectedDevices.length > 0 && (
<div className="text-base text-gray-500">
{formData.selectedDevices.length}
<div className="text-sm text-gray-400 mt-1">
{formData.selectedDevices.map(id => {
// 这里可以根据实际API获取设备名称
return `设备 ${id}`;
}).join(', ')}
</div>
</div>
)}
{formData.selectedLibraries.length > 0 && (
<div className="text-base text-gray-500">{formData.selectedLibraries.join(', ')}</div>
)}
<div className="flex space-x-4 pt-4">
<Button variant="outline" onClick={handlePrev} className="flex-1 h-12 rounded-xl text-base">
</Button>
<Button
onClick={handleComplete}
loading={loading}
className="flex-1 h-12 bg-blue-600 hover:bg-blue-700 rounded-xl text-base shadow-sm"
>
{loading ? '创建中...' : '完成'}
</Button>
</div>
<div className="flex space-x-4 pt-4">
<Button variant="outline" onClick={handlePrev} className="flex-1 h-12 rounded-xl text-base">
</Button>
<Button
onClick={handleNext}
className="flex-1 h-12 bg-blue-600 hover:bg-blue-700 rounded-xl text-base shadow-sm"
>
</Button>
</div>
)}
<DeviceSelectionDialog
open={deviceDialogOpen}
onOpenChange={setDeviceDialogOpen}
selectedDevices={formData.selectedDevices}
onSelect={(devices) => {
handleUpdateFormData({ selectedDevices: devices });
}}
/>
</div>
)}
{currentStep === 3 && (
<div className="space-y-6">
<div className="relative">
<Search className="absolute left-3 top-4 h-5 w-5 text-gray-400" />
<Input
placeholder="选择内容库"
className="h-12 pl-11 rounded-xl border-gray-200 text-base"
onClick={() => setContentLibraryDialogOpen(true)}
readOnly
/>
</div>
{formData.selectedLibraries.length > 0 && (
<div className="text-base text-gray-500">
{formData.selectedLibraries.length}
<div className="text-sm text-gray-400 mt-1">
{formData.selectedLibraries.map(id => {
// 这里可以根据实际API获取内容库名称
return `朋友圈内容库${id}`;
}).join(', ')}
</div>
</div>
)}
<div className="flex space-x-4 pt-4">
<Button variant="outline" onClick={handlePrev} className="flex-1 h-12 rounded-xl text-base">
</Button>
<Button
onClick={handleComplete}
loading={loading}
className="flex-1 h-12 bg-blue-600 hover:bg-blue-700 rounded-xl text-base shadow-sm"
>
{loading ? '创建中...' : '完成'}
</Button>
</div>
<ContentLibrarySelectionDialog
open={contentLibraryDialogOpen}
onOpenChange={setContentLibraryDialogOpen}
selectedLibraries={formData.selectedLibraries}
onSelect={(libraries) => {
handleUpdateFormData({ selectedLibraries: libraries });
}}
/>
</div>
)}
</div>
</div>
</div>