feat: 本次提交更新内容如下 存一下进度
This commit is contained in:
@@ -178,7 +178,7 @@ const AutoLike: React.FC = () => {
|
||||
|
||||
// 查看任务
|
||||
const handleView = (taskId: string) => {
|
||||
navigate(`/workspace/auto-like/detail/${taskId}`);
|
||||
navigate(`/workspace/auto-like/record/${taskId}`);
|
||||
};
|
||||
|
||||
// 复制任务
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
PlusOutlined,
|
||||
MinusOutlined,
|
||||
ArrowLeftOutlined,
|
||||
CheckOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Button, Input, Switch, message, Spin } from "antd";
|
||||
import { NavBar } from "antd-mobile";
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
} from "./api";
|
||||
import {
|
||||
CreateLikeTaskData,
|
||||
UpdateLikeTaskData,
|
||||
ContentType,
|
||||
} from "@/pages/workspace/auto-like/record/api";
|
||||
import style from "./new.module.scss";
|
||||
@@ -27,6 +27,12 @@ const contentTypeLabels: Record<ContentType, string> = {
|
||||
link: "链接",
|
||||
};
|
||||
|
||||
const steps = [
|
||||
{ title: "基础设置", desc: "设置点赞规则" },
|
||||
{ title: "设备选择", desc: "选择执行设备" },
|
||||
{ title: "好友设置", desc: "选择目标人群" },
|
||||
];
|
||||
|
||||
const NewAutoLike: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -122,21 +128,57 @@ const NewAutoLike: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 步骤器
|
||||
const renderStepIndicator = () => (
|
||||
<div className={style.stepIndicatorWrapper}>
|
||||
<div className={style.stepIndicator}>
|
||||
{steps.map((s, i) => (
|
||||
<div
|
||||
key={s.title}
|
||||
className={
|
||||
style.stepItem +
|
||||
" " +
|
||||
(currentStep === i + 1
|
||||
? style.stepActive
|
||||
: i + 1 < currentStep
|
||||
? style.stepDone
|
||||
: "")
|
||||
}
|
||||
>
|
||||
<span className={style.stepNum}>
|
||||
{i + 1 < currentStep ? <CheckOutlined /> : i + 1}
|
||||
</span>
|
||||
<span className={style.stepTitle}>{s.title}</span>
|
||||
<span className={style.stepDesc}>{s.desc}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={style.stepProgressBarBg}>
|
||||
<div
|
||||
className={style.stepProgressBar}
|
||||
style={{
|
||||
width: `${((currentStep - 1) / (steps.length - 1)) * 100}%`,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// 步骤1:基础设置
|
||||
const renderBasicSettings = () => (
|
||||
<div className={style["form-section"]}>
|
||||
<div className={style["form-item"]}>
|
||||
<label className={style["form-label"]}>任务名称</label>
|
||||
<div className={style.basicSection}>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>任务名称</div>
|
||||
<Input
|
||||
placeholder="请输入任务名称"
|
||||
value={formData.name}
|
||||
onChange={(e) => handleUpdateFormData({ name: e.target.value })}
|
||||
className={style["form-input"]}
|
||||
className={style.input}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["form-item"]}>
|
||||
<label className={style["form-label"]}>点赞间隔</label>
|
||||
<div className={style["stepper-group"]}>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>点赞间隔</div>
|
||||
<div className={style.counterRow}>
|
||||
<Button
|
||||
icon={<MinusOutlined />}
|
||||
onClick={() =>
|
||||
@@ -144,21 +186,21 @@ const NewAutoLike: React.FC = () => {
|
||||
interval: Math.max(1, formData.interval - 1),
|
||||
})
|
||||
}
|
||||
className={style["stepper-btn"]}
|
||||
className={style.counterBtn}
|
||||
/>
|
||||
<span className={style["stepper-value"]}>{formData.interval} 秒</span>
|
||||
<span className={style.counterValue}>{formData.interval} 秒</span>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() =>
|
||||
handleUpdateFormData({ interval: formData.interval + 1 })
|
||||
}
|
||||
className={style["stepper-btn"]}
|
||||
className={style.counterBtn}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style["form-item"]}>
|
||||
<label className={style["form-label"]}>每日上限</label>
|
||||
<div className={style["stepper-group"]}>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>每日最大点赞数</div>
|
||||
<div className={style.counterRow}>
|
||||
<Button
|
||||
icon={<MinusOutlined />}
|
||||
onClick={() =>
|
||||
@@ -166,48 +208,48 @@ const NewAutoLike: React.FC = () => {
|
||||
maxLikes: Math.max(1, formData.maxLikes - 10),
|
||||
})
|
||||
}
|
||||
className={style["stepper-btn"]}
|
||||
className={style.counterBtn}
|
||||
/>
|
||||
<span className={style["stepper-value"]}>{formData.maxLikes} 次</span>
|
||||
<span className={style.counterValue}>{formData.maxLikes} 次</span>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() =>
|
||||
handleUpdateFormData({ maxLikes: formData.maxLikes + 10 })
|
||||
}
|
||||
className={style["stepper-btn"]}
|
||||
className={style.counterBtn}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style["form-item"]}>
|
||||
<label className={style["form-label"]}>执行时间</label>
|
||||
<div className={style["time-range"]}>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>点赞时间范围</div>
|
||||
<div className={style.timeRow}>
|
||||
<Input
|
||||
type="time"
|
||||
value={formData.startTime}
|
||||
onChange={(e) =>
|
||||
handleUpdateFormData({ startTime: e.target.value })
|
||||
}
|
||||
className={style["time-input"]}
|
||||
className={style.inputTime}
|
||||
/>
|
||||
<span className={style["time-separator"]}>至</span>
|
||||
<span className={style.timeTo}>至</span>
|
||||
<Input
|
||||
type="time"
|
||||
value={formData.endTime}
|
||||
onChange={(e) => handleUpdateFormData({ endTime: e.target.value })}
|
||||
className={style["time-input"]}
|
||||
className={style.inputTime}
|
||||
/>
|
||||
</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) => (
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>点赞内容类型</div>
|
||||
<div className={style.contentTypes}>
|
||||
{(["text", "image", "video"] as ContentType[]).map((type) => (
|
||||
<span
|
||||
key={type}
|
||||
className={
|
||||
formData.contentTypes.includes(type)
|
||||
? style["content-type-tag-active"]
|
||||
: style["content-type-tag"]
|
||||
? style.contentTypeTagActive
|
||||
: style.contentTypeTag
|
||||
}
|
||||
onClick={() => {
|
||||
const newTypes = formData.contentTypes.includes(type)
|
||||
@@ -221,88 +263,125 @@ const NewAutoLike: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={style["form-item"]}>
|
||||
<label className={style["form-label"]}>自动开启</label>
|
||||
<Switch checked={autoEnabled} onChange={setAutoEnabled} />
|
||||
<div className={style.formItem}>
|
||||
<div className={style.switchRow}>
|
||||
<span className={style.switchLabel}>启用好友标签</span>
|
||||
<Switch
|
||||
checked={formData.enableFriendTags}
|
||||
onChange={(checked) =>
|
||||
handleUpdateFormData({ enableFriendTags: checked })
|
||||
}
|
||||
className={style.switch}
|
||||
/>
|
||||
</div>
|
||||
{formData.enableFriendTags && (
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>好友标签</div>
|
||||
<Input
|
||||
placeholder="请输入标签"
|
||||
value={formData.friendTags}
|
||||
onChange={(e) =>
|
||||
handleUpdateFormData({ friendTags: e.target.value })
|
||||
}
|
||||
className={style.input}
|
||||
/>
|
||||
<div className={style.counterTip}>只给有此标签的好友点赞</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.switchRow}>
|
||||
<span className={style.switchLabel}>自动开启</span>
|
||||
<Switch
|
||||
checked={autoEnabled}
|
||||
onChange={setAutoEnabled}
|
||||
className={style.switch}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["form-actions"]}>
|
||||
<Button
|
||||
type="primary"
|
||||
block
|
||||
onClick={handleNext}
|
||||
size="large"
|
||||
className={style["main-btn"]}
|
||||
>
|
||||
下一步
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// 步骤2:设备选择(占位)
|
||||
// 步骤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 className={style.basicSection}>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>选择设备</div>
|
||||
<Input
|
||||
placeholder="请选择设备"
|
||||
value={formData.devices.join(", ")}
|
||||
readOnly
|
||||
onClick={() => message.info("这里应弹出设备选择器")}
|
||||
className={style.input}
|
||||
/>
|
||||
{formData.devices.length > 0 && (
|
||||
<div className={style.selectedTip}>
|
||||
已选设备: {formData.devices.length}个
|
||||
</div>
|
||||
</div>
|
||||
<div className={style["form-actions"]}>
|
||||
<Button
|
||||
onClick={handlePrev}
|
||||
size="large"
|
||||
className={style["secondary-btn"]}
|
||||
>
|
||||
上一步
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleNext}
|
||||
size="large"
|
||||
className={style["main-btn"]}
|
||||
>
|
||||
下一步
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// 步骤3:好友设置(占位)
|
||||
// 步骤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 className={style.basicSection}>
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>选择微信好友</div>
|
||||
<Input
|
||||
placeholder="请选择微信好友"
|
||||
value={formData.friends.join(", ")}
|
||||
readOnly
|
||||
onClick={() => message.info("这里应弹出好友选择器")}
|
||||
className={style.input}
|
||||
/>
|
||||
{formData.friends.length > 0 && (
|
||||
<div className={style.selectedTip}>
|
||||
已选好友: {formData.friends.length}个
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={style["form-actions"]}>
|
||||
<Button
|
||||
onClick={handlePrev}
|
||||
size="large"
|
||||
className={style["secondary-btn"]}
|
||||
>
|
||||
);
|
||||
|
||||
// 底部按钮
|
||||
const renderFooterBtn = () => (
|
||||
<div className={style.footerBtnBar}>
|
||||
{currentStep > 1 && (
|
||||
<Button onClick={handlePrev} className={style.prevBtn}>
|
||||
上一步
|
||||
</Button>
|
||||
)}
|
||||
{currentStep < 3 && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleNext}
|
||||
className={style.nextBtn}
|
||||
disabled={
|
||||
(currentStep === 1 && !formData.name.trim()) ||
|
||||
(currentStep === 2 && formData.devices.length === 0)
|
||||
}
|
||||
>
|
||||
下一步
|
||||
</Button>
|
||||
)}
|
||||
{currentStep === 3 && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleComplete}
|
||||
size="large"
|
||||
loading={isSubmitting}
|
||||
className={style["main-btn"]}
|
||||
className={style.completeBtn}
|
||||
>
|
||||
{isEditMode ? "更新任务" : "创建任务"}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
<>
|
||||
<NavBar
|
||||
back={null}
|
||||
style={{ background: "#fff" }}
|
||||
@@ -319,24 +398,24 @@ const NewAutoLike: React.FC = () => {
|
||||
{isEditMode ? "编辑自动点赞" : "新建自动点赞"}
|
||||
</span>
|
||||
</NavBar>
|
||||
{renderStepIndicator()}
|
||||
</>
|
||||
}
|
||||
footer={renderFooterBtn()}
|
||||
>
|
||||
<div className={style["new-page-bg"]}>
|
||||
<div className={style["new-page-center"]}>
|
||||
{/* 步骤器保留新项目的 */}
|
||||
{/* 你可以在这里插入新项目的步骤器组件 */}
|
||||
<div className={style["form-card"]}>
|
||||
<div className={style.formBg}>
|
||||
{isLoading ? (
|
||||
<div className={style.formLoading}>
|
||||
<Spin />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{currentStep === 1 && renderBasicSettings()}
|
||||
{currentStep === 2 && renderDeviceSelection()}
|
||||
{currentStep === 3 && renderFriendSettings()}
|
||||
{isLoading && (
|
||||
<div className={style["loading"]}>
|
||||
<Spin />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
.formBg {
|
||||
background: #f8f6f3;
|
||||
min-height: 100vh;
|
||||
padding: 32px 0 32px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 0 80px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.formSteps {
|
||||
.stepIndicatorWrapper {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
background: #f8f6f3;
|
||||
padding: 16px 0 8px 0;
|
||||
}
|
||||
|
||||
.stepIndicator {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 32px;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.formStepIndicator {
|
||||
.stepItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -22,18 +27,19 @@
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
transition: color 0.2s;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.formStepActive {
|
||||
.stepActive {
|
||||
color: #188eee;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.formStepDone {
|
||||
.stepDone {
|
||||
color: #19c37d;
|
||||
}
|
||||
|
||||
.formStepNum {
|
||||
.stepNum {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
@@ -46,24 +52,56 @@
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.formStepActive .formStepNum {
|
||||
.stepActive .stepNum {
|
||||
background: #188eee;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.formStepDone .formStepNum {
|
||||
.stepDone .stepNum {
|
||||
background: #19c37d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.formStep {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
padding: 32px 24px 24px 24px;
|
||||
.stepTitle {
|
||||
font-size: 14px;
|
||||
margin-top: 2px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stepDesc {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
margin-top: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stepProgressBarBg {
|
||||
position: relative;
|
||||
width: 80%;
|
||||
height: 4px;
|
||||
background: #e5e7eb;
|
||||
border-radius: 2px;
|
||||
margin: 12px auto 0 auto;
|
||||
}
|
||||
|
||||
.stepProgressBar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
background: #188eee;
|
||||
border-radius: 2px;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
.basicSection {
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
padding: 24px 16px 0 16px;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
margin: 0 auto 24px auto;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.formItem {
|
||||
@@ -225,3 +263,24 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footerBtnBar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 30;
|
||||
background: #fff;
|
||||
box-shadow: 0 -2px 8px rgba(0,0,0,0.04);
|
||||
padding: 16px 24px 24px 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.prevBtn, .nextBtn, .completeBtn {
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
min-width: 120px;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ const workspaceRoutes = [
|
||||
auth: true,
|
||||
},
|
||||
{
|
||||
path: "/workspace/auto-like/:id",
|
||||
path: "/workspace/auto-like/record/:id",
|
||||
element: <RecordAutoLike />,
|
||||
auth: true,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user