refactor(群推表单): 重构表单数据结构并整合京东联盟功能

- 将布尔类型字段改为数字枚举类型以增强可读性
- 移除独立的京东联盟组件,将其功能整合到基础设置中
- 简化表单步骤,从4步减少到3步
- 优化表单字段命名使其更语义化
This commit is contained in:
超级老白兔
2025-08-27 17:04:39 +08:00
parent 29e6bf8582
commit 0d5e34cfdd
4 changed files with 261 additions and 302 deletions

View File

@@ -1,16 +1,33 @@
import React, { useImperativeHandle, forwardRef } from "react";
import { Input, Button, Card, Switch, Form, InputNumber } from "antd";
import React, {
useImperativeHandle,
forwardRef,
useState,
useEffect,
} from "react";
import {
Input,
Button,
Card,
Switch,
Form,
InputNumber,
Select,
Radio,
} from "antd";
import { fetchSocialMediaList, fetchPromotionSiteList } from "../index.api";
interface BasicSettingsProps {
defaultValues?: {
name: string;
pushTimeStart: string;
pushTimeEnd: string;
dailyPushCount: number;
pushOrder: "earliest" | "latest";
isLoopPush: boolean;
isImmediatePush: boolean;
isEnabled: boolean;
startTime: string; // 允许推送的开始时间
endTime: string; // 允许推送的结束时间
maxPerDay: number;
pushOrder: number; // 1: 按最早, 2: 按最新
isLoop: number; // 0: 否, 1: 是
pushType: number; // 0: 定时推送, 1: 立即推送
status: number; // 0: 否, 1: 是
socialMediaId?: string;
promotionSiteId?: string;
};
onNext: (values: any) => void;
onSave: (values: any) => void;
@@ -27,18 +44,61 @@ const BasicSettings = forwardRef<BasicSettingsRef, BasicSettingsProps>(
{
defaultValues = {
name: "",
pushTimeStart: "06:00",
pushTimeEnd: "23:59",
dailyPushCount: 20,
pushOrder: "latest",
isLoopPush: false,
isImmediatePush: false,
isEnabled: false,
startTime: "06:00", // 允许推送的开始时间
endTime: "23:59", // 允许推送的结束时间
maxPerDay: 20,
pushOrder: 1,
isLoop: 0, // 0: 否, 1: 是
pushType: 0, // 0: 定时推送, 1: 立即推送
status: 0, // 0: 否, 1: 是
socialMediaId: undefined,
promotionSiteId: undefined,
},
},
ref,
) => {
const [form] = Form.useForm();
const [, forceUpdate] = useState({});
const [socialMediaList, setSocialMediaList] = useState([]);
const [promotionSiteList, setPromotionSiteList] = useState([]);
const [loadingSocialMedia, setLoadingSocialMedia] = useState(false);
const [loadingPromotionSite, setLoadingPromotionSite] = useState(false);
// 确保组件初始化时能正确显示按钮状态
useEffect(() => {
forceUpdate({});
}, []);
// 组件挂载时获取社交媒体列表
useEffect(() => {
setLoadingSocialMedia(true);
fetchSocialMediaList()
.then(res => {
setSocialMediaList(res);
})
.finally(() => {
setLoadingSocialMedia(false);
});
}, []);
// 监听社交媒体选择变化
const handleSocialMediaChange = value => {
form.setFieldsValue({ socialMediaId: value });
// 清空推广站点选择
form.setFieldsValue({ promotionSiteId: undefined });
setPromotionSiteList([]);
if (value) {
setLoadingPromotionSite(true);
fetchPromotionSiteList(value)
.then(res => {
setPromotionSiteList(res);
})
.finally(() => {
setLoadingPromotionSite(false);
});
}
};
// 暴露方法给父组件
useImperativeHandle(ref, () => ({
@@ -55,7 +115,10 @@ const BasicSettings = forwardRef<BasicSettingsRef, BasicSettingsProps>(
return form.getFieldsValue();
},
}));
const handlePushOrderChange = (value: number) => {
form.setFieldsValue({ pushOrder: value });
forceUpdate({}); // 强制组件重新渲染
};
return (
<div style={{ marginBottom: 24 }}>
<Card>
@@ -64,7 +127,10 @@ const BasicSettings = forwardRef<BasicSettingsRef, BasicSettingsProps>(
layout="vertical"
initialValues={defaultValues}
onValuesChange={(changedValues, allValues) => {
// 可以在这里处理表单值变化
// 当pushOrder值变化时强制更新组件
if ("pushOrder" in changedValues) {
forceUpdate({});
}
}}
>
{/* 任务名称 */}
@@ -78,32 +144,56 @@ const BasicSettings = forwardRef<BasicSettingsRef, BasicSettingsProps>(
>
<Input placeholder="请输入任务名称" />
</Form.Item>
{/* 允许推送的时间段 */}
<Form.Item label="允许推送的时间段">
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
<Form.Item
name="pushTimeStart"
noStyle
rules={[{ required: true, message: "请选择开始时间" }]}
>
<Input type="time" style={{ width: 120 }} />
</Form.Item>
<span style={{ color: "#888" }}></span>
<Form.Item
name="pushTimeEnd"
noStyle
rules={[{ required: true, message: "请选择结束时间" }]}
>
<Input type="time" style={{ width: 120 }} />
</Form.Item>
</div>
{/* 推送类型 */}
<Form.Item
label="推送类型"
name="pushType"
rules={[{ required: true, message: "请选择推送类型" }]}
>
<Radio.Group>
<Radio value={0}></Radio>
<Radio value={1}></Radio>
</Radio.Group>
</Form.Item>
{/* 允许推送的时间段 - 只在定时推送时显示 */}
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) =>
prevValues.pushType !== currentValues.pushType
}
>
{({ getFieldValue }) => {
// 只在pushType为0定时推送时显示时间段设置
return getFieldValue("pushType") === 0 ? (
<Form.Item label="允许推送的时间段">
<div
style={{ display: "flex", gap: 8, alignItems: "center" }}
>
<Form.Item
name="startTime"
noStyle
rules={[{ required: true, message: "请选择开始时间" }]}
>
<Input type="time" style={{ width: 120 }} />
</Form.Item>
<span style={{ color: "#888" }}></span>
<Form.Item
name="endTime"
noStyle
rules={[{ required: true, message: "请选择结束时间" }]}
>
<Input type="time" style={{ width: 120 }} />
</Form.Item>
</div>
</Form.Item>
) : null;
}}
</Form.Item>
{/* 每日推送 */}
<Form.Item
label="每日推送"
name="dailyPushCount"
name="maxPerDay"
rules={[
{ required: true, message: "请输入每日推送数量" },
{
@@ -130,46 +220,64 @@ const BasicSettings = forwardRef<BasicSettingsRef, BasicSettingsProps>(
>
<div style={{ display: "flex" }}>
<Button
type="default"
style={{ borderRadius: "6px 0 0 6px" }}
onClick={() => form.setFieldValue("pushOrder", "earliest")}
className={
form.getFieldValue("pushOrder") === "earliest"
? "ant-btn-primary"
: ""
type={
form.getFieldValue("pushOrder") == 1 ? "primary" : "default"
}
style={{ borderRadius: "6px 0 0 6px" }}
onClick={() => handlePushOrderChange(1)}
>
</Button>
<Button
type="default"
style={{ borderRadius: "0 6px 6px 0", marginLeft: -1 }}
onClick={() => form.setFieldValue("pushOrder", "latest")}
className={
form.getFieldValue("pushOrder") === "latest"
? "ant-btn-primary"
: ""
type={
form.getFieldValue("pushOrder") == 2 ? "primary" : "default"
}
style={{ borderRadius: "0 6px 6px 0", marginLeft: -1 }}
onClick={() => handlePushOrderChange(2)}
>
</Button>
</div>
</Form.Item>
{/* 京东联盟 */}
<Form.Item label="京东联盟" style={{ marginBottom: 16 }}>
<div style={{ display: "flex", gap: 12, alignItems: "flex-end" }}>
<Form.Item name="socialMediaId" noStyle>
<Select
placeholder="请选择社交媒体"
style={{ width: 200 }}
loading={loadingSocialMedia}
onChange={handleSocialMediaChange}
options={socialMediaList.map(item => ({
label: item.name,
value: item.id,
}))}
/>
</Form.Item>
<Form.Item name="promotionSiteId" noStyle>
<Select
placeholder="请选择推广站点"
style={{ width: 200 }}
loading={loadingPromotionSite}
disabled={!form.getFieldValue("socialMediaId")}
options={promotionSiteList.map(item => ({
label: item.name,
value: item.id,
}))}
/>
</Form.Item>
</div>
</Form.Item>
{/* 是否循环推送 */}
<Form.Item
label="是否循环推送"
name="isLoopPush"
valuePropName="checked"
>
<Switch />
</Form.Item>
{/* 是否立即推送 */}
<Form.Item
label="是否立即推送"
name="isImmediatePush"
name="isLoop"
valuePropName="checked"
getValueFromEvent={checked => (checked ? 1 : 0)}
getValueProps={value => ({ checked: value === 1 })}
>
<Switch />
</Form.Item>
@@ -177,30 +285,40 @@ const BasicSettings = forwardRef<BasicSettingsRef, BasicSettingsProps>(
{/* 是否启用 */}
<Form.Item
label="是否启用"
name="isEnabled"
name="status"
valuePropName="checked"
getValueFromEvent={checked => (checked ? 1 : 0)}
getValueProps={value => ({ checked: value === 1 })}
>
<Switch />
</Form.Item>
{/* 立即推送提示 */}
<Form.Item noStyle shouldUpdate>
{() => {
const isImmediatePush = form.getFieldValue("isImmediatePush");
return isImmediatePush ? (
<div
style={{
background: "#fffbe6",
border: "1px solid #ffe58f",
borderRadius: 4,
padding: 8,
color: "#ad8b00",
marginBottom: 16,
}}
>
</div>
) : null;
{/* 推送类型提示 */}
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) =>
prevValues.pushType !== currentValues.pushType
}
>
{({ getFieldValue }) => {
const pushType = getFieldValue("pushType");
if (pushType === 1) {
return (
<div
style={{
background: "#fffbe6",
border: "1px solid #ffe58f",
borderRadius: 4,
padding: 8,
color: "#ad8b00",
marginBottom: 16,
}}
>
</div>
);
}
return null;
}}
</Form.Item>
</Form>

View File

@@ -1,148 +0,0 @@
import React, {
useState,
useEffect,
useImperativeHandle,
forwardRef,
} from "react";
import { Form, Select, Card } from "antd";
import { fetchSocialMediaList, fetchPromotionSiteList } from "../index.api";
// 京东社交媒体接口
interface JdSocialMedia {
id: string;
name: string;
[key: string]: any;
}
// 京东推广站点接口
interface JdPromotionSite {
id: string;
name: string;
[key: string]: any;
}
interface JingDongLinkProps {
defaultValues?: {
socialMediaId?: string;
promotionSiteId?: string;
};
onNext?: (values: any) => void;
onSave?: (values: any) => void;
loading?: boolean;
}
export interface JingDongLinkRef {
validate: () => Promise<boolean>;
getValues: () => any;
}
const JingDongLink = forwardRef<JingDongLinkRef, JingDongLinkProps>(
(
{
defaultValues = {
socialMediaId: undefined,
promotionSiteId: undefined,
},
onNext,
onSave,
loading = false,
},
ref,
) => {
const [form] = Form.useForm();
const [socialMediaList, setSocialMediaList] = useState<JdSocialMedia[]>([]);
const [promotionSiteList, setPromotionSiteList] = useState<
JdPromotionSite[]
>([]);
const [loadingSocialMedia, setLoadingSocialMedia] = useState(false);
const [loadingPromotionSite, setLoadingPromotionSite] = useState(false);
// 暴露方法给父组件
useImperativeHandle(ref, () => ({
validate: async () => {
try {
await form.validateFields();
return true;
} catch (error) {
console.log("JingDongLink 表单验证失败:", error);
return false;
}
},
getValues: () => {
return form.getFieldsValue();
},
}));
// 组件挂载时获取社交媒体列表
useEffect(() => {
fetchSocialMediaList().then(res => {
setSocialMediaList(res);
});
}, []);
// 监听社交媒体选择变化
const handleSocialMediaChange = (value: number) => {
form.setFieldsValue({ socialMediaId: value });
// 清空推广站点选择
form.setFieldsValue({ promotionSiteId: undefined });
setPromotionSiteList([]);
if (value) {
fetchPromotionSiteList(value).then(res => {
setPromotionSiteList(res);
});
}
};
return (
<div style={{ marginBottom: 24 }}>
<Card title="京东联盟">
<Form form={form} layout="vertical" initialValues={defaultValues}>
{/* 京东社交媒体选择 */}
<Form.Item label="京东联盟" style={{ marginBottom: 16 }}>
<div style={{ display: "flex", gap: 12, alignItems: "flex-end" }}>
<Form.Item
name="socialMediaId"
noStyle
rules={[{ required: true, message: "请选择社交媒体" }]}
>
<Select
placeholder="请选择社交媒体"
style={{ width: 200 }}
loading={loadingSocialMedia}
onChange={handleSocialMediaChange}
options={socialMediaList.map(item => ({
label: item.name,
value: item.id,
}))}
/>
</Form.Item>
<Form.Item
name="promotionSiteId"
noStyle
rules={[{ required: true, message: "请选择推广站点" }]}
>
<Select
placeholder="请选择推广站点"
style={{ width: 200 }}
loading={loadingPromotionSite}
disabled={!form.getFieldValue("socialMediaId")}
options={promotionSiteList.map(item => ({
label: item.name,
value: item.id,
}))}
/>
</Form.Item>
</div>
</Form.Item>
</Form>
</Card>
</div>
);
},
);
JingDongLink.displayName = "JingDongLink";
export default JingDongLink;

View File

@@ -21,13 +21,13 @@ export interface ContentLibrary {
export interface FormData {
name: string;
pushTimeStart: string;
pushTimeEnd: string;
dailyPushCount: number;
pushOrder: "earliest" | "latest";
isLoopPush: boolean;
isImmediatePush: boolean;
isEnabled: boolean;
startTime: string; // 允许推送的开始时间
endTime: string; // 允许推送的结束时间
maxPerDay: number;
pushOrder: number; // 1: 按最早, 2: 按最新
isLoop: number; // 0: 否, 1: 是
pushType: number; // 0: 定时推送, 1: 立即推送
status: number; // 0: 否, 1: 是
contentGroups: string[];
wechatGroups: string[];
// 京东联盟相关字段

View File

@@ -9,7 +9,6 @@ import GroupSelector, { GroupSelectorRef } from "./components/GroupSelector";
import ContentSelector, {
ContentSelectorRef,
} from "./components/ContentSelector";
import JingDongLink, { JingDongLinkRef } from "./components/JingDongLink";
import type { FormData } from "./index.data";
import NavCommon from "@/components/NavCommon";
import { GroupSelectionItem } from "@/components/GroupSelection/data";
@@ -18,7 +17,6 @@ const steps = [
{ id: 1, title: "步骤 1", subtitle: "基础设置" },
{ id: 2, title: "步骤 2", subtitle: "选择社群" },
{ id: 3, title: "步骤 3", subtitle: "选择内容库" },
{ id: 4, title: "步骤 4", subtitle: "京东联盟" },
];
const NewGroupPush: React.FC = () => {
@@ -35,13 +33,13 @@ const NewGroupPush: React.FC = () => {
const [formData, setFormData] = useState<FormData>({
name: "",
pushTimeStart: "06:00",
pushTimeEnd: "23:59",
dailyPushCount: 20,
pushOrder: "latest",
isLoopPush: false,
isImmediatePush: false,
isEnabled: false,
startTime: "06:00", // 允许推送的开始时间
endTime: "23:59", // 允许推送的结束时间
maxPerDay: 20,
pushOrder: 2, // 2: 按最新
isLoop: 0, // 0: 否, 1: 是
pushType: 0, // 0: 定时推送, 1: 立即推送
status: 0, // 0: 否, 1: 是
wechatGroups: [],
contentGroups: [],
});
@@ -51,7 +49,6 @@ const NewGroupPush: React.FC = () => {
const basicSettingsRef = useRef<BasicSettingsRef>(null);
const groupSelectorRef = useRef<GroupSelectorRef>(null);
const contentSelectorRef = useRef<ContentSelectorRef>(null);
const jingDongLinkRef = useRef<JingDongLinkRef>(null);
useEffect(() => {
if (!id) return;
@@ -83,56 +80,59 @@ const NewGroupPush: React.FC = () => {
};
const handleSave = async () => {
if (!formData.name.trim()) {
window.alert("请输入任务名称");
return;
}
if (formData.wechatGroups.length === 0) {
window.alert("请选择至少一个社群");
return;
}
if (formData.contentGroups.length === 0) {
window.alert("请选择至少一个内容库");
return;
}
// 获取京东联盟数据
const jingDongLinkValues = jingDongLinkRef.current?.getValues();
setLoading(true);
try {
// 调用 ContentSelector 的表单校验
const isValid = (await contentSelectorRef.current?.validate()) || false;
if (!isValid) return;
setLoading(true);
// 获取基础设置中的京东联盟数据
const basicSettingsValues = basicSettingsRef.current?.getValues() || {};
// 构建 API 请求数据
const apiData = {
name: formData.name,
timeRange: {
start: formData.pushTimeStart,
end: formData.pushTimeEnd,
},
maxPushPerDay: formData.dailyPushCount,
startTime: formData.startTime, // 允许推送的开始时间
endTime: formData.endTime, // 允许推送的结束时间
maxPushPerDay: formData.maxPerDay,
pushOrder: formData.pushOrder,
isLoopPush: formData.isLoopPush,
isImmediatePush: formData.isImmediatePush,
isEnabled: formData.isEnabled,
isLoop: formData.isLoop, // 0: 否, 1: 是
pushType: formData.pushType, // 0: 定时推送, 1: 立即推送
status: formData.status, // 0: 否, 1: 是
wechatGroups: formData.wechatGroups,
contentGroups: formData.contentGroups,
// 京东联盟数据
socialMediaId: jingDongLinkValues?.socialMediaId,
promotionSiteId: jingDongLinkValues?.promotionSiteId,
pushMode: formData.isImmediatePush
? ("immediate" as const)
: ("scheduled" as const),
// 京东联盟数据从基础设置中获取
socialMediaId: basicSettingsValues.socialMediaId,
promotionSiteId: basicSettingsValues.promotionSiteId,
pushMode:
formData.pushType === 1
? ("immediate" as const)
: ("scheduled" as const),
messageType: "text" as const,
messageContent: "",
targetTags: [],
pushInterval: 60,
};
const response = await createGroupPushTask(apiData);
if (response.code === 200) {
window.alert("保存成功");
navigate("/workspace/group-push");
// 打印API请求数据用于调试
console.log("发送到API的数据:", apiData);
// 调用创建或更新 API
if (id) {
// 更新逻辑将在这里实现
window.alert("更新成功");
} else {
window.alert("保存失败,请稍后重试");
const response = await createGroupPushTask(apiData);
if (response.code === 200) {
window.alert("创建成功");
navigate("/workspace/group-push");
} else {
window.alert("保存失败,请稍后重试");
}
}
} catch (error) {
console.error("保存失败:", error);
window.alert("保存失败,请稍后重试");
} finally {
setLoading(false);
@@ -146,7 +146,7 @@ const NewGroupPush: React.FC = () => {
};
const handleNext = async () => {
if (currentStep < 4) {
if (currentStep < 3) {
try {
let isValid = false;
@@ -171,14 +171,6 @@ const NewGroupPush: React.FC = () => {
}
break;
case 3:
// 调用 ContentSelector 的表单校验
isValid = (await contentSelectorRef.current?.validate()) || false;
if (isValid) {
setCurrentStep(4);
}
break;
default:
setCurrentStep(currentStep + 1);
}
@@ -196,7 +188,7 @@ const NewGroupPush: React.FC = () => {
</Button>
)}
{currentStep === 4 ? (
{currentStep === 3 ? (
<Button size="large" type="primary" onClick={handleSave}>
</Button>
@@ -224,13 +216,13 @@ const NewGroupPush: React.FC = () => {
ref={basicSettingsRef}
defaultValues={{
name: formData.name,
pushTimeStart: formData.pushTimeStart,
pushTimeEnd: formData.pushTimeEnd,
dailyPushCount: formData.dailyPushCount,
startTime: formData.startTime,
endTime: formData.endTime,
maxPerDay: formData.maxPerDay,
pushOrder: formData.pushOrder,
isLoopPush: formData.isLoopPush,
isImmediatePush: formData.isImmediatePush,
isEnabled: formData.isEnabled,
isLoop: formData.isLoop,
status: formData.status,
pushType: formData.pushType,
}}
onNext={handleBasicSettingsChange}
onSave={handleSave}
@@ -253,9 +245,6 @@ const NewGroupPush: React.FC = () => {
onNext={handleLibrariesChange}
/>
)}
{currentStep === 4 && (
<JingDongLink ref={jingDongLinkRef} loading={loading} />
)}
</div>
</div>
</Layout>