feat: 本次提交更新内容如下

自动点赞列表构建完成
This commit is contained in:
笔记本里的永平
2025-07-21 17:47:22 +08:00
parent 94f61085ca
commit 5f9ffadc37
15 changed files with 2500 additions and 81 deletions

View File

@@ -0,0 +1,437 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
NavBar,
Button,
Toast,
SpinLoading,
Form,
Input,
Switch,
Stepper,
Card,
Tag,
} from "antd-mobile";
import { Input as AntInput, TimePicker, Select } from "antd";
import {
PlusOutlined,
MinusOutlined,
CheckOutlined,
TagOutlined,
ClockCircleOutlined,
LikeOutlined,
UserOutlined,
SettingOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
import MeauMobile from "@/components/MeauMobile/MeauMoible";
import {
createAutoLikeTask,
updateAutoLikeTask,
fetchAutoLikeTaskDetail,
} from "@/api/autoLike";
import {
CreateLikeTaskData,
UpdateLikeTaskData,
ContentType,
} from "@/types/auto-like";
import style from "./new.module.scss";
const { Option } = Select;
const NewAutoLike: React.FC = () => {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
const isEditMode = !!id;
const [currentStep, setCurrentStep] = useState(1);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isLoading, setIsLoading] = useState(isEditMode);
const [autoEnabled, setAutoEnabled] = useState(false);
const [formData, setFormData] = useState<CreateLikeTaskData>({
name: "",
interval: 5,
maxLikes: 200,
startTime: "08:00",
endTime: "22:00",
contentTypes: ["text", "image", "video"],
devices: [],
friends: [],
targetTags: [],
friendMaxLikes: 10,
enableFriendTags: false,
friendTags: "",
});
// 如果是编辑模式,获取任务详情
useEffect(() => {
if (isEditMode && id) {
fetchTaskDetail();
}
}, [id, isEditMode]);
// 获取任务详情
const fetchTaskDetail = async () => {
try {
const taskDetail = await fetchAutoLikeTaskDetail(id!);
if (taskDetail) {
const taskAny = taskDetail as any;
const config = taskAny.config || taskAny;
setFormData({
name: taskDetail.name || "",
interval: config.likeInterval || config.interval || 5,
maxLikes: config.maxLikesPerDay || config.maxLikes || 200,
startTime: config.timeRange?.start || config.startTime || "08:00",
endTime: config.timeRange?.end || config.endTime || "22:00",
contentTypes: config.contentTypes || ["text", "image", "video"],
devices: config.devices || [],
friends: config.friends || [],
targetTags: config.targetTags || [],
friendMaxLikes: config.friendMaxLikes || 10,
enableFriendTags: config.enableFriendTags || false,
friendTags: config.friendTags || "",
});
const status = taskAny.status;
setAutoEnabled(status === 1 || status === "running");
}
} catch (error) {
Toast.show({
content: "获取任务详情失败",
position: "top",
});
} finally {
setIsLoading(false);
}
};
const handleUpdateFormData = (data: Partial<CreateLikeTaskData>) => {
setFormData((prev) => ({ ...prev, ...data }));
};
const handleNext = () => {
if (currentStep < 3) {
setCurrentStep(currentStep + 1);
}
};
const handlePrev = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
}
};
const handleComplete = async () => {
if (!formData.name.trim()) {
Toast.show({
content: "请输入任务名称",
position: "top",
});
return;
}
if (formData.devices.length === 0) {
Toast.show({
content: "请选择执行设备",
position: "top",
});
return;
}
setIsSubmitting(true);
try {
if (isEditMode && id) {
const updateData: UpdateLikeTaskData = {
...formData,
id,
};
await updateAutoLikeTask(updateData);
Toast.show({
content: "更新成功",
position: "top",
});
} else {
await createAutoLikeTask(formData);
Toast.show({
content: "创建成功",
position: "top",
});
}
navigate("/workspace/auto-like");
} catch (error) {
Toast.show({
content: isEditMode ? "更新失败" : "创建失败",
position: "top",
});
} finally {
setIsSubmitting(false);
}
};
const handleContentTypeChange = (type: ContentType) => {
const newTypes = formData.contentTypes.includes(type)
? formData.contentTypes.filter((t) => t !== type)
: [...formData.contentTypes, type];
handleUpdateFormData({ contentTypes: newTypes });
};
const renderStepIndicator = () => (
<div className={style["step-indicator"]}>
<div className={style["step-list"]}>
<div
className={`${style["step-item"]} ${currentStep >= 1 ? style["active"] : ""}`}
>
<div className={style["step-number"]}>1</div>
<div className={style["step-label"]}></div>
</div>
<div
className={`${style["step-item"]} ${currentStep >= 2 ? style["active"] : ""}`}
>
<div className={style["step-number"]}>2</div>
<div className={style["step-label"]}></div>
</div>
<div
className={`${style["step-item"]} ${currentStep >= 3 ? style["active"] : ""}`}
>
<div className={style["step-number"]}>3</div>
<div className={style["step-label"]}></div>
</div>
</div>
</div>
);
const renderBasicSettings = () => (
<div className={style["step-content"]}>
<Card className={style["form-card"]}>
<div className={style["form-item"]}>
<label className={style["form-label"]}></label>
<Input
placeholder="请输入任务名称"
value={formData.name}
onChange={(value) => handleUpdateFormData({ name: value })}
className={style["form-input"]}
/>
</div>
<div className={style["form-item"]}>
<label className={style["form-label"]}></label>
<div className={style["stepper-wrapper"]}>
<Button
size="small"
onClick={() =>
handleUpdateFormData({
interval: Math.max(1, formData.interval - 1),
})
}
className={style["stepper-btn"]}
>
<MinusOutlined />
</Button>
<span className={style["stepper-value"]}>
{formData.interval}
</span>
<Button
size="small"
onClick={() =>
handleUpdateFormData({ interval: formData.interval + 1 })
}
className={style["stepper-btn"]}
>
<PlusOutlined />
</Button>
</div>
</div>
<div className={style["form-item"]}>
<label className={style["form-label"]}></label>
<div className={style["stepper-wrapper"]}>
<Button
size="small"
onClick={() =>
handleUpdateFormData({
maxLikes: Math.max(1, formData.maxLikes - 10),
})
}
className={style["stepper-btn"]}
>
<MinusOutlined />
</Button>
<span className={style["stepper-value"]}>
{formData.maxLikes}
</span>
<Button
size="small"
onClick={() =>
handleUpdateFormData({ maxLikes: formData.maxLikes + 10 })
}
className={style["stepper-btn"]}
>
<PlusOutlined />
</Button>
</div>
</div>
<div className={style["form-item"]}>
<label className={style["form-label"]}></label>
<div className={style["time-range"]}>
<Input
type="time"
value={formData.startTime}
onChange={(value) => handleUpdateFormData({ startTime: value })}
className={style["time-input"]}
/>
<span className={style["time-separator"]}></span>
<Input
type="time"
value={formData.endTime}
onChange={(value) => handleUpdateFormData({ endTime: value })}
className={style["time-input"]}
/>
</div>
</div>
<div className={style["form-item"]}>
<label className={style["form-label"]}></label>
<div className={style["content-types"]}>
{(["text", "image", "video", "link"] as ContentType[]).map(
(type) => (
<Tag
key={type}
color={
formData.contentTypes.includes(type) ? "primary" : "default"
}
onClick={() => handleContentTypeChange(type)}
className={style["content-type-tag"]}
>
{type === "text" && "文字"}
{type === "image" && "图片"}
{type === "video" && "视频"}
{type === "link" && "链接"}
</Tag>
)
)}
</div>
</div>
<div className={style["form-item"]}>
<label className={style["form-label"]}></label>
<Switch
checked={autoEnabled}
onChange={setAutoEnabled}
className={style["form-switch"]}
/>
</div>
</Card>
</div>
);
const renderDeviceSelection = () => (
<div className={style["step-content"]}>
<Card className={style["form-card"]}>
<div className={style["placeholder-content"]}>
<SettingOutlined className={style["placeholder-icon"]} />
<div className={style["placeholder-text"]}>...</div>
<div className={style["placeholder-subtext"]}>
{formData.devices.length}
</div>
</div>
</Card>
</div>
);
const renderFriendSettings = () => (
<div className={style["step-content"]}>
<Card className={style["form-card"]}>
<div className={style["placeholder-content"]}>
<UserOutlined className={style["placeholder-icon"]} />
<div className={style["placeholder-text"]}>...</div>
<div className={style["placeholder-subtext"]}>
{formData.friends.length}
</div>
</div>
</Card>
</div>
);
if (isLoading) {
return (
<Layout
header={
<NavBar
backArrow
style={{ background: "#fff" }}
onBack={() => window.history.back()}
left={
<div style={{ color: "var(--primary-color)", fontWeight: 600 }}>
{isEditMode ? "编辑任务" : "新建任务"}
</div>
}
/>
}
footer={<MeauMobile />}
>
<div className={style["loading"]}>
<SpinLoading color="primary" />
<div className={style["loading-text"]}>...</div>
</div>
</Layout>
);
}
return (
<Layout
header={
<NavBar
backArrow
style={{ background: "#fff" }}
onBack={() => window.history.back()}
left={
<div style={{ color: "var(--primary-color)", fontWeight: 600 }}>
{isEditMode ? "编辑任务" : "新建任务"}
</div>
}
/>
}
footer={<MeauMobile />}
>
<div className={style["new-page"]}>
{renderStepIndicator()}
{currentStep === 1 && renderBasicSettings()}
{currentStep === 2 && renderDeviceSelection()}
{currentStep === 3 && renderFriendSettings()}
<div className={style["step-actions"]}>
{currentStep > 1 && (
<Button onClick={handlePrev} className={style["prev-btn"]}>
</Button>
)}
{currentStep < 3 ? (
<Button
color="primary"
onClick={handleNext}
className={style["next-btn"]}
>
</Button>
) : (
<Button
color="primary"
onClick={handleComplete}
loading={isSubmitting}
className={style["complete-btn"]}
>
{isEditMode ? "更新任务" : "创建任务"}
</Button>
)}
</div>
</div>
</Layout>
);
};
export default NewAutoLike;