feat: 本次提交更新内容如下
先存一版本
This commit is contained in:
21
nkebao/src/pages/workspace/auto-like/new/api.ts
Normal file
21
nkebao/src/pages/workspace/auto-like/new/api.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import request from "@/api/request";
|
||||||
|
import {
|
||||||
|
CreateLikeTaskData,
|
||||||
|
UpdateLikeTaskData,
|
||||||
|
LikeTask,
|
||||||
|
} from "@/types/auto-like";
|
||||||
|
|
||||||
|
// 获取自动点赞任务详情
|
||||||
|
export function fetchAutoLikeTaskDetail(id: string): Promise<LikeTask | null> {
|
||||||
|
return request("/v1/workbench/detail", { id }, "GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建自动点赞任务
|
||||||
|
export function createAutoLikeTask(data: CreateLikeTaskData): Promise<any> {
|
||||||
|
return request("/v1/workbench/create", { ...data, type: 1 }, "POST");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新自动点赞任务
|
||||||
|
export function updateAutoLikeTask(data: UpdateLikeTaskData): Promise<any> {
|
||||||
|
return request("/v1/workbench/update", { ...data, type: 1 }, "POST");
|
||||||
|
}
|
||||||
@@ -1,36 +1,16 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
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 {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
MinusOutlined,
|
MinusOutlined,
|
||||||
CheckOutlined,
|
ArrowLeftOutlined,
|
||||||
TagOutlined,
|
|
||||||
ClockCircleOutlined,
|
|
||||||
LikeOutlined,
|
|
||||||
UserOutlined,
|
|
||||||
SettingOutlined,
|
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
|
import { Button, Input, Switch, message, Spin } from "antd";
|
||||||
import Layout from "@/components/Layout/Layout";
|
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
|
||||||
import {
|
import {
|
||||||
createAutoLikeTask,
|
createAutoLikeTask,
|
||||||
updateAutoLikeTask,
|
updateAutoLikeTask,
|
||||||
fetchAutoLikeTaskDetail,
|
fetchAutoLikeTaskDetail,
|
||||||
} from "@/api/autoLike";
|
} from "./api";
|
||||||
import {
|
import {
|
||||||
CreateLikeTaskData,
|
CreateLikeTaskData,
|
||||||
UpdateLikeTaskData,
|
UpdateLikeTaskData,
|
||||||
@@ -38,7 +18,12 @@ import {
|
|||||||
} from "@/types/auto-like";
|
} from "@/types/auto-like";
|
||||||
import style from "./new.module.scss";
|
import style from "./new.module.scss";
|
||||||
|
|
||||||
const { Option } = Select;
|
const contentTypeLabels: Record<ContentType, string> = {
|
||||||
|
text: "文字",
|
||||||
|
image: "图片",
|
||||||
|
video: "视频",
|
||||||
|
link: "链接",
|
||||||
|
};
|
||||||
|
|
||||||
const NewAutoLike: React.FC = () => {
|
const NewAutoLike: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -48,7 +33,6 @@ const NewAutoLike: React.FC = () => {
|
|||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(isEditMode);
|
const [isLoading, setIsLoading] = useState(isEditMode);
|
||||||
const [autoEnabled, setAutoEnabled] = useState(false);
|
const [autoEnabled, setAutoEnabled] = useState(false);
|
||||||
|
|
||||||
const [formData, setFormData] = useState<CreateLikeTaskData>({
|
const [formData, setFormData] = useState<CreateLikeTaskData>({
|
||||||
name: "",
|
name: "",
|
||||||
interval: 5,
|
interval: 5,
|
||||||
@@ -64,21 +48,18 @@ const NewAutoLike: React.FC = () => {
|
|||||||
friendTags: "",
|
friendTags: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果是编辑模式,获取任务详情
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEditMode && id) {
|
if (isEditMode && id) {
|
||||||
fetchTaskDetail();
|
fetchTaskDetail();
|
||||||
}
|
}
|
||||||
}, [id, isEditMode]);
|
}, [id, isEditMode]);
|
||||||
|
|
||||||
// 获取任务详情
|
|
||||||
const fetchTaskDetail = async () => {
|
const fetchTaskDetail = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const taskDetail = await fetchAutoLikeTaskDetail(id!);
|
const taskDetail = await fetchAutoLikeTaskDetail(id!);
|
||||||
if (taskDetail) {
|
if (taskDetail) {
|
||||||
const taskAny = taskDetail as any;
|
const config = (taskDetail as any).config || taskDetail;
|
||||||
const config = taskAny.config || taskAny;
|
|
||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
name: taskDetail.name || "",
|
name: taskDetail.name || "",
|
||||||
interval: config.likeInterval || config.interval || 5,
|
interval: config.likeInterval || config.interval || 5,
|
||||||
@@ -93,15 +74,14 @@ const NewAutoLike: React.FC = () => {
|
|||||||
enableFriendTags: config.enableFriendTags || false,
|
enableFriendTags: config.enableFriendTags || false,
|
||||||
friendTags: config.friendTags || "",
|
friendTags: config.friendTags || "",
|
||||||
});
|
});
|
||||||
|
setAutoEnabled(
|
||||||
const status = taskAny.status;
|
(taskDetail as any).status === 1 ||
|
||||||
setAutoEnabled(status === 1 || status === "running");
|
(taskDetail as any).status === "running"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Toast.show({
|
message.error("获取任务详情失败");
|
||||||
content: "获取任务详情失败",
|
navigate("/workspace/auto-like");
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -111,326 +91,246 @@ const NewAutoLike: React.FC = () => {
|
|||||||
setFormData((prev) => ({ ...prev, ...data }));
|
setFormData((prev) => ({ ...prev, ...data }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => setCurrentStep((prev) => Math.min(prev + 1, 3));
|
||||||
if (currentStep < 3) {
|
const handlePrev = () => setCurrentStep((prev) => Math.max(prev - 1, 1));
|
||||||
setCurrentStep(currentStep + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePrev = () => {
|
|
||||||
if (currentStep > 1) {
|
|
||||||
setCurrentStep(currentStep - 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleComplete = async () => {
|
const handleComplete = async () => {
|
||||||
if (!formData.name.trim()) {
|
if (!formData.name.trim()) {
|
||||||
Toast.show({
|
message.warning("请输入任务名称");
|
||||||
content: "请输入任务名称",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!formData.devices || formData.devices.length === 0) {
|
||||||
if (formData.devices.length === 0) {
|
message.warning("请选择执行设备");
|
||||||
Toast.show({
|
|
||||||
content: "请选择执行设备",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
if (isEditMode && id) {
|
if (isEditMode && id) {
|
||||||
const updateData: UpdateLikeTaskData = {
|
await updateAutoLikeTask({ ...formData, id });
|
||||||
...formData,
|
message.success("更新成功");
|
||||||
id,
|
|
||||||
};
|
|
||||||
await updateAutoLikeTask(updateData);
|
|
||||||
Toast.show({
|
|
||||||
content: "更新成功",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
await createAutoLikeTask(formData);
|
await createAutoLikeTask(formData);
|
||||||
Toast.show({
|
message.success("创建成功");
|
||||||
content: "创建成功",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
navigate("/workspace/auto-like");
|
navigate("/workspace/auto-like");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Toast.show({
|
message.error(isEditMode ? "更新失败" : "创建失败");
|
||||||
content: isEditMode ? "更新失败" : "创建失败",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContentTypeChange = (type: ContentType) => {
|
// 顶部导航栏
|
||||||
const newTypes = formData.contentTypes.includes(type)
|
const renderNavBar = () => (
|
||||||
? formData.contentTypes.filter((t) => t !== type)
|
<div className={style["nav-bar"]}>
|
||||||
: [...formData.contentTypes, type];
|
<Button
|
||||||
handleUpdateFormData({ contentTypes: newTypes });
|
type="text"
|
||||||
};
|
icon={<ArrowLeftOutlined />}
|
||||||
|
className={style["nav-back-btn"]}
|
||||||
const renderStepIndicator = () => (
|
onClick={() => navigate(-1)}
|
||||||
<div className={style["step-indicator"]}>
|
/>
|
||||||
<div className={style["step-list"]}>
|
<span className={style["nav-title"]}>
|
||||||
<div
|
{isEditMode ? "编辑自动点赞" : "新建自动点赞"}
|
||||||
className={`${style["step-item"]} ${currentStep >= 1 ? style["active"] : ""}`}
|
</span>
|
||||||
>
|
|
||||||
<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>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 步骤1:基础设置
|
||||||
const renderBasicSettings = () => (
|
const renderBasicSettings = () => (
|
||||||
<div className={style["step-content"]}>
|
<div className={style["form-section"]}>
|
||||||
<Card className={style["form-card"]}>
|
|
||||||
<div className={style["form-item"]}>
|
<div className={style["form-item"]}>
|
||||||
<label className={style["form-label"]}>任务名称</label>
|
<label className={style["form-label"]}>任务名称</label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="请输入任务名称"
|
placeholder="请输入任务名称"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(value) => handleUpdateFormData({ name: value })}
|
onChange={(e) => handleUpdateFormData({ name: e.target.value })}
|
||||||
className={style["form-input"]}
|
className={style["form-input"]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style["form-item"]}>
|
<div className={style["form-item"]}>
|
||||||
<label className={style["form-label"]}>点赞间隔</label>
|
<label className={style["form-label"]}>点赞间隔</label>
|
||||||
<div className={style["stepper-wrapper"]}>
|
<div className={style["stepper-group"]}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
icon={<MinusOutlined />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleUpdateFormData({
|
handleUpdateFormData({
|
||||||
interval: Math.max(1, formData.interval - 1),
|
interval: Math.max(1, formData.interval - 1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className={style["stepper-btn"]}
|
className={style["stepper-btn"]}
|
||||||
>
|
/>
|
||||||
<MinusOutlined />
|
<span className={style["stepper-value"]}>{formData.interval} 秒</span>
|
||||||
</Button>
|
|
||||||
<span className={style["stepper-value"]}>
|
|
||||||
{formData.interval} 秒
|
|
||||||
</span>
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
icon={<PlusOutlined />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleUpdateFormData({ interval: formData.interval + 1 })
|
handleUpdateFormData({ interval: formData.interval + 1 })
|
||||||
}
|
}
|
||||||
className={style["stepper-btn"]}
|
className={style["stepper-btn"]}
|
||||||
>
|
/>
|
||||||
<PlusOutlined />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style["form-item"]}>
|
<div className={style["form-item"]}>
|
||||||
<label className={style["form-label"]}>每日上限</label>
|
<label className={style["form-label"]}>每日上限</label>
|
||||||
<div className={style["stepper-wrapper"]}>
|
<div className={style["stepper-group"]}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
icon={<MinusOutlined />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleUpdateFormData({
|
handleUpdateFormData({
|
||||||
maxLikes: Math.max(1, formData.maxLikes - 10),
|
maxLikes: Math.max(1, formData.maxLikes - 10),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className={style["stepper-btn"]}
|
className={style["stepper-btn"]}
|
||||||
>
|
/>
|
||||||
<MinusOutlined />
|
<span className={style["stepper-value"]}>{formData.maxLikes} 次</span>
|
||||||
</Button>
|
|
||||||
<span className={style["stepper-value"]}>
|
|
||||||
{formData.maxLikes} 次
|
|
||||||
</span>
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
icon={<PlusOutlined />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleUpdateFormData({ maxLikes: formData.maxLikes + 10 })
|
handleUpdateFormData({ maxLikes: formData.maxLikes + 10 })
|
||||||
}
|
}
|
||||||
className={style["stepper-btn"]}
|
className={style["stepper-btn"]}
|
||||||
>
|
/>
|
||||||
<PlusOutlined />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style["form-item"]}>
|
<div className={style["form-item"]}>
|
||||||
<label className={style["form-label"]}>执行时间</label>
|
<label className={style["form-label"]}>执行时间</label>
|
||||||
<div className={style["time-range"]}>
|
<div className={style["time-range"]}>
|
||||||
<Input
|
<Input
|
||||||
type="time"
|
type="time"
|
||||||
value={formData.startTime}
|
value={formData.startTime}
|
||||||
onChange={(value) => handleUpdateFormData({ startTime: value })}
|
onChange={(e) =>
|
||||||
|
handleUpdateFormData({ startTime: e.target.value })
|
||||||
|
}
|
||||||
className={style["time-input"]}
|
className={style["time-input"]}
|
||||||
/>
|
/>
|
||||||
<span className={style["time-separator"]}>至</span>
|
<span className={style["time-separator"]}>至</span>
|
||||||
<Input
|
<Input
|
||||||
type="time"
|
type="time"
|
||||||
value={formData.endTime}
|
value={formData.endTime}
|
||||||
onChange={(value) => handleUpdateFormData({ endTime: value })}
|
onChange={(e) => handleUpdateFormData({ endTime: e.target.value })}
|
||||||
className={style["time-input"]}
|
className={style["time-input"]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style["form-item"]}>
|
<div className={style["form-item"]}>
|
||||||
<label className={style["form-label"]}>内容类型</label>
|
<label className={style["form-label"]}>内容类型</label>
|
||||||
<div className={style["content-types"]}>
|
<div className={style["content-types"]}>
|
||||||
{(["text", "image", "video", "link"] as ContentType[]).map(
|
{(["text", "image", "video", "link"] as ContentType[]).map((type) => (
|
||||||
(type) => (
|
<span
|
||||||
<Tag
|
|
||||||
key={type}
|
key={type}
|
||||||
color={
|
className={
|
||||||
formData.contentTypes.includes(type) ? "primary" : "default"
|
formData.contentTypes.includes(type)
|
||||||
|
? style["content-type-tag-active"]
|
||||||
|
: style["content-type-tag"]
|
||||||
}
|
}
|
||||||
onClick={() => handleContentTypeChange(type)}
|
onClick={() => {
|
||||||
className={style["content-type-tag"]}
|
const newTypes = formData.contentTypes.includes(type)
|
||||||
|
? formData.contentTypes.filter((t) => t !== type)
|
||||||
|
: [...formData.contentTypes, type];
|
||||||
|
handleUpdateFormData({ contentTypes: newTypes });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{type === "text" && "文字"}
|
{contentTypeLabels[type]}
|
||||||
{type === "image" && "图片"}
|
</span>
|
||||||
{type === "video" && "视频"}
|
))}
|
||||||
{type === "link" && "链接"}
|
|
||||||
</Tag>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style["form-item"]}>
|
<div className={style["form-item"]}>
|
||||||
<label className={style["form-label"]}>自动开启</label>
|
<label className={style["form-label"]}>自动开启</label>
|
||||||
<Switch
|
<Switch checked={autoEnabled} onChange={setAutoEnabled} />
|
||||||
checked={autoEnabled}
|
|
||||||
onChange={setAutoEnabled}
|
|
||||||
className={style["form-switch"]}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
<div className={style["form-actions"]}>
|
||||||
</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
|
<Button
|
||||||
color="primary"
|
type="primary"
|
||||||
|
block
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
className={style["next-btn"]}
|
size="large"
|
||||||
|
className={style["main-btn"]}
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 步骤2:设备选择(占位)
|
||||||
|
const renderDeviceSelection = () => (
|
||||||
|
<div className={style["form-section"]}>
|
||||||
|
<div className={style["placeholder-content"]}>
|
||||||
|
<span className={style["placeholder-icon"]}>[设备选择组件占位]</span>
|
||||||
|
<div className={style["placeholder-text"]}>设备选择功能开发中...</div>
|
||||||
|
<div className={style["placeholder-subtext"]}>
|
||||||
|
当前已选择 {formData.devices?.length || 0} 个设备
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={style["form-actions"]}>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
onClick={handlePrev}
|
||||||
|
size="large"
|
||||||
|
className={style["secondary-btn"]}
|
||||||
|
>
|
||||||
|
上一步
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={handleNext}
|
||||||
|
size="large"
|
||||||
|
className={style["main-btn"]}
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 步骤3:好友设置(占位)
|
||||||
|
const renderFriendSettings = () => (
|
||||||
|
<div className={style["form-section"]}>
|
||||||
|
<div className={style["placeholder-content"]}>
|
||||||
|
<span className={style["placeholder-icon"]}>[好友选择组件占位]</span>
|
||||||
|
<div className={style["placeholder-text"]}>好友设置功能开发中...</div>
|
||||||
|
<div className={style["placeholder-subtext"]}>
|
||||||
|
当前已选择 {formData.friends?.length || 0} 个好友
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={style["form-actions"]}>
|
||||||
|
<Button
|
||||||
|
onClick={handlePrev}
|
||||||
|
size="large"
|
||||||
|
className={style["secondary-btn"]}
|
||||||
|
>
|
||||||
|
上一步
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
onClick={handleComplete}
|
onClick={handleComplete}
|
||||||
|
size="large"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
className={style["complete-btn"]}
|
className={style["main-btn"]}
|
||||||
>
|
>
|
||||||
{isEditMode ? "更新任务" : "创建任务"}
|
{isEditMode ? "更新任务" : "创建任务"}
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style["new-page-bg"]}>
|
||||||
|
{renderNavBar()}
|
||||||
|
<div className={style["new-page-center"]}>
|
||||||
|
{/* 步骤器保留新项目的 */}
|
||||||
|
{/* 你可以在这里插入新项目的步骤器组件 */}
|
||||||
|
<div className={style["form-card"]}>
|
||||||
|
{currentStep === 1 && renderBasicSettings()}
|
||||||
|
{currentStep === 2 && renderDeviceSelection()}
|
||||||
|
{currentStep === 3 && renderFriendSettings()}
|
||||||
|
{isLoading && (
|
||||||
|
<div className={style["loading"]}>
|
||||||
|
<Spin />
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,144 +1,118 @@
|
|||||||
.new-page {
|
.new-page-bg {
|
||||||
background: #f5f5f5;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding-bottom: 80px;
|
background: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-indicator {
|
.nav-bar {
|
||||||
background: white;
|
|
||||||
padding: 20px 16px;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-list {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
height: 56px;
|
||||||
|
background: #fff;
|
||||||
&::before {
|
box-shadow: 0 1px 0 #f0f0f0;
|
||||||
content: '';
|
padding: 0 24px;
|
||||||
position: absolute;
|
position: sticky;
|
||||||
top: 20px;
|
top: 0;
|
||||||
left: 20px;
|
z-index: 10;
|
||||||
right: 20px;
|
|
||||||
height: 2px;
|
|
||||||
background: #e5e5e5;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-item {
|
.nav-back-btn {
|
||||||
display: flex;
|
border: none;
|
||||||
flex-direction: column;
|
background: none;
|
||||||
align-items: center;
|
font-size: 20px;
|
||||||
gap: 8px;
|
color: #222;
|
||||||
position: relative;
|
margin-right: 8px;
|
||||||
z-index: 2;
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
&.active {
|
min-width: 32px;
|
||||||
.step-number {
|
min-height: 32px;
|
||||||
background: #1890ff;
|
|
||||||
color: white;
|
|
||||||
border-color: #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-label {
|
|
||||||
color: #1890ff;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-number {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: white;
|
|
||||||
border: 2px solid #e5e5e5;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 16px;
|
}
|
||||||
|
|
||||||
|
.nav-title {
|
||||||
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #999;
|
color: #222;
|
||||||
transition: all 0.3s;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-label {
|
.new-page-center {
|
||||||
font-size: 12px;
|
display: flex;
|
||||||
color: #666;
|
flex-direction: column;
|
||||||
text-align: center;
|
align-items: center;
|
||||||
transition: all 0.3s;
|
margin-top: 32px;
|
||||||
}
|
|
||||||
|
|
||||||
.step-content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-card {
|
.form-card {
|
||||||
background: white;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 18px;
|
||||||
padding: 20px;
|
box-shadow: 0 2px 16px rgba(0,0,0,0.06);
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
padding: 36px 32px 32px 32px;
|
||||||
|
min-width: 340px;
|
||||||
|
max-width: 420px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item {
|
.form-item {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 28px;
|
||||||
|
display: flex;
|
||||||
&:last-child {
|
flex-direction: column;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
display: block;
|
font-size: 15px;
|
||||||
font-size: 14px;
|
font-weight: 500;
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input {
|
.form-input {
|
||||||
width: 100%;
|
border-radius: 10px !important;
|
||||||
height: 40px;
|
height: 44px;
|
||||||
border: 1px solid #d9d9d9;
|
font-size: 15px;
|
||||||
border-radius: 6px;
|
padding-left: 14px;
|
||||||
padding: 0 12px;
|
background: #f8f9fa;
|
||||||
font-size: 14px;
|
border: 1px solid #e5e6eb;
|
||||||
|
transition: border 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
&:focus {
|
.form-input:focus {
|
||||||
border-color: #1890ff;
|
border-color: #1890ff;
|
||||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
background: #fff;
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepper-wrapper {
|
.stepper-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepper-btn {
|
.stepper-btn {
|
||||||
width: 32px;
|
border-radius: 8px !important;
|
||||||
height: 32px;
|
width: 36px;
|
||||||
padding: 0;
|
height: 36px;
|
||||||
border-radius: 6px;
|
font-size: 18px;
|
||||||
|
background: #f5f6fa;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
color: #222;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 1px solid #d9d9d9;
|
transition: border 0.2s;
|
||||||
background: white;
|
}
|
||||||
|
|
||||||
&:hover {
|
.stepper-btn:hover {
|
||||||
border-color: #1890ff;
|
border-color: #1890ff;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.stepper-value {
|
.stepper-value {
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: #333;
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
@@ -148,65 +122,108 @@
|
|||||||
.time-range {
|
.time-range {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-input {
|
.time-input {
|
||||||
flex: 1;
|
border-radius: 10px !important;
|
||||||
height: 40px;
|
height: 44px;
|
||||||
border: 1px solid #d9d9d9;
|
font-size: 15px;
|
||||||
border-radius: 6px;
|
padding-left: 14px;
|
||||||
padding: 0 12px;
|
background: #f8f9fa;
|
||||||
font-size: 14px;
|
border: 1px solid #e5e6eb;
|
||||||
|
width: 120px;
|
||||||
&:focus {
|
|
||||||
border-color: #1890ff;
|
|
||||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-separator {
|
.time-separator {
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
color: #666;
|
color: #888;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-types {
|
.content-types {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-type-tag {
|
.content-type-tag {
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f5f6fa;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 6px 18px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-switch {
|
.content-type-tag-active {
|
||||||
margin-left: auto;
|
border-radius: 8px;
|
||||||
|
background: #e6f4ff;
|
||||||
|
color: #1890ff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 6px 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #1890ff;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-btn {
|
||||||
|
border-radius: 10px !important;
|
||||||
|
height: 44px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #1890ff;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 8px rgba(24,144,255,0.08);
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-btn:hover {
|
||||||
|
background: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-btn {
|
||||||
|
border-radius: 10px !important;
|
||||||
|
height: 44px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #fff;
|
||||||
|
border: 1.5px solid #e5e6eb;
|
||||||
|
color: #222;
|
||||||
|
transition: border 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-btn:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-content {
|
.placeholder-content {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 40px 20px;
|
color: #888;
|
||||||
color: #666;
|
padding: 40px 0 24px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-icon {
|
.placeholder-icon {
|
||||||
font-size: 48px;
|
font-size: 32px;
|
||||||
color: #d9d9d9;
|
color: #d9d9d9;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 12px;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-text {
|
.placeholder-text {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 8px;
|
|
||||||
color: #333;
|
color: #333;
|
||||||
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-subtext {
|
.placeholder-subtext {
|
||||||
@@ -214,88 +231,20 @@
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-actions {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: white;
|
|
||||||
padding: 16px;
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prev-btn {
|
|
||||||
flex: 1;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.next-btn,
|
|
||||||
.complete-btn {
|
|
||||||
flex: 1;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 60vh;
|
min-height: 120px;
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-text {
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动端适配
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.step-list {
|
|
||||||
&::before {
|
|
||||||
left: 15px;
|
|
||||||
right: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-number {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-label {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
.form-card {
|
.form-card {
|
||||||
padding: 16px;
|
min-width: 0;
|
||||||
|
max-width: 100vw;
|
||||||
|
padding: 18px 6px 18px 6px;
|
||||||
}
|
}
|
||||||
|
.new-page-center {
|
||||||
.stepper-wrapper {
|
margin-top: 12px;
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stepper-btn {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-range {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-types {
|
|
||||||
gap: 6px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user