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