Merge branch 'yongpxu-dev' into yongpxu-dev2
# Conflicts: # Cunkebao/dist/.vite/manifest.json # Cunkebao/dist/index.html
This commit is contained in:
@@ -10,7 +10,7 @@ export interface FriendSelectionItem {
|
||||
export interface FriendSelectionProps {
|
||||
selectedOptions?: FriendSelectionItem[];
|
||||
onSelect: (friends: FriendSelectionItem[]) => void;
|
||||
deviceIds?: string[];
|
||||
deviceIds?: number[];
|
||||
enableDeviceFilter?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
|
||||
@@ -12,7 +12,7 @@ interface SelectionPopupProps {
|
||||
onVisibleChange: (visible: boolean) => void;
|
||||
selectedOptions: FriendSelectionItem[];
|
||||
onSelect: (friends: FriendSelectionItem[]) => void;
|
||||
deviceIds?: string[];
|
||||
deviceIds?: number[];
|
||||
enableDeviceFilter?: boolean;
|
||||
readonly?: boolean;
|
||||
onConfirm?: (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { Modal, Selector } from "antd-mobile";
|
||||
import { Popup, Selector } from "antd-mobile";
|
||||
import type { PackageOption } from "./data";
|
||||
|
||||
interface BatchAddModalProps {
|
||||
@@ -15,20 +15,30 @@ interface BatchAddModalProps {
|
||||
const BatchAddModal: React.FC<BatchAddModalProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
packageOptions,
|
||||
packageOptions = [],
|
||||
batchTarget,
|
||||
setBatchTarget,
|
||||
selectedCount,
|
||||
onConfirm,
|
||||
}) => (
|
||||
<Modal
|
||||
// <Modal visible={visible} title="批量加入分组" onConfirm={onConfirm}>
|
||||
// <div style={{ marginBottom: 12 }}>
|
||||
// <div>选择目标分组</div>
|
||||
// <Selector
|
||||
// options={packageOptions.map(p => ({ label: p.name, value: p.id }))}
|
||||
// value={[batchTarget]}
|
||||
// onChange={v => setBatchTarget(v[0])}
|
||||
// />
|
||||
// </div>
|
||||
// <div style={{ color: "#888", fontSize: 13 }}>
|
||||
// 将选中的{selectedCount}个用户加入所选分组
|
||||
// </div>
|
||||
// </Modal>
|
||||
<Popup
|
||||
visible={visible}
|
||||
title="批量加入分组"
|
||||
onClose={onClose}
|
||||
footer={[
|
||||
{ text: "取消", onClick: onClose },
|
||||
{ text: "确定", onClick: onConfirm },
|
||||
]}
|
||||
onMaskClick={() => onClose()}
|
||||
position="bottom"
|
||||
bodyStyle={{ height: "80vh" }}
|
||||
>
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<div>选择目标分组</div>
|
||||
@@ -41,7 +51,7 @@ const BatchAddModal: React.FC<BatchAddModalProps> = ({
|
||||
<div style={{ color: "#888", fontSize: 13 }}>
|
||||
将选中的{selectedCount}个用户加入所选分组
|
||||
</div>
|
||||
</Modal>
|
||||
</Popup>
|
||||
);
|
||||
|
||||
export default BatchAddModal;
|
||||
|
||||
@@ -9,10 +9,10 @@ export function fetchTrafficPoolList(params: {
|
||||
return request("/v1/traffic/pool", params, "GET");
|
||||
}
|
||||
|
||||
export async function fetchScenarioOptions(): Promise<any[]> {
|
||||
export async function fetchScenarioOptions() {
|
||||
return request("/v1/plan/scenes", {}, "GET");
|
||||
}
|
||||
|
||||
export async function fetchPackageOptions(): Promise<any[]> {
|
||||
export async function fetchPackageOptions() {
|
||||
return request("/v1/traffic/pool/getPackage", {}, "GET");
|
||||
}
|
||||
|
||||
@@ -4,14 +4,7 @@ import {
|
||||
fetchPackageOptions,
|
||||
fetchScenarioOptions,
|
||||
} from "./api";
|
||||
import type {
|
||||
TrafficPoolUser,
|
||||
DeviceOption,
|
||||
PackageOption,
|
||||
ValueLevel,
|
||||
UserStatus,
|
||||
ScenarioOption,
|
||||
} from "./data";
|
||||
import type { TrafficPoolUser, PackageOption, ScenarioOption } from "./data";
|
||||
import { Toast } from "antd-mobile";
|
||||
|
||||
export function useTrafficPoolListLogic() {
|
||||
@@ -78,8 +71,12 @@ export function useTrafficPoolListLogic() {
|
||||
|
||||
// 获取筛选项
|
||||
useEffect(() => {
|
||||
fetchPackageOptions().then(setPackageOptions);
|
||||
fetchScenarioOptions().then(setScenarioOptions);
|
||||
fetchPackageOptions().then(res => {
|
||||
setPackageOptions(res.list || []);
|
||||
});
|
||||
fetchScenarioOptions().then(res => {
|
||||
setScenarioOptions(res.list || []);
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 筛选条件变化时刷新列表
|
||||
|
||||
@@ -173,8 +173,8 @@ const TrafficPoolList: React.FC = () => {
|
||||
status: "offline" as const,
|
||||
})),
|
||||
);
|
||||
setPackageId(filters.packageId);
|
||||
setScenarioId(filters.scenarioId);
|
||||
setPackageId(filters.packageId ? parseInt(filters.packageId) : 0);
|
||||
setScenarioId(filters.scenarioId ? parseInt(filters.scenarioId) : 0);
|
||||
setUserValue(filters.userValue);
|
||||
setUserStatus(filters.userStatus);
|
||||
// 重新获取列表
|
||||
|
||||
@@ -35,8 +35,8 @@ export function deleteAutoLikeTask(id: string): Promise<any> {
|
||||
}
|
||||
|
||||
// 切换任务状态
|
||||
export function toggleAutoLikeTask(id: string, status: string): Promise<any> {
|
||||
return request("/v1/workbench/update-status", { id, status }, "POST");
|
||||
export function toggleAutoLikeTask(data): Promise<any> {
|
||||
return request("/v1/workbench/update-status", { ...data, type: 1 }, "POST");
|
||||
}
|
||||
|
||||
// 复制自动点赞任务
|
||||
|
||||
@@ -79,9 +79,9 @@ export interface CreateLikeTaskData {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
contentTypes: ContentType[];
|
||||
deveiceGroups: string[];
|
||||
deveiceGroups: number[];
|
||||
deveiceGroupsOptions: DeviceSelectionItem[];
|
||||
friendsGroups: string[];
|
||||
friendsGroups: number[];
|
||||
friendsGroupsOptions: FriendSelectionItem[];
|
||||
friendMaxLikes: number;
|
||||
friendTags?: string;
|
||||
|
||||
@@ -330,7 +330,12 @@ const NewAutoLike: React.FC = () => {
|
||||
<div className={style.formItem}>
|
||||
<DeviceSelection
|
||||
selectedOptions={formData.deveiceGroupsOptions}
|
||||
onSelect={devices => handleUpdateFormData({ devices })}
|
||||
onSelect={devices =>
|
||||
handleUpdateFormData({
|
||||
deveiceGroups: devices.map(v => v.id),
|
||||
deveiceGroupsOptions: devices,
|
||||
})
|
||||
}
|
||||
showInput={true}
|
||||
showSelectedList={true}
|
||||
/>
|
||||
@@ -363,7 +368,7 @@ const NewAutoLike: React.FC = () => {
|
||||
selectedOptions={formData.friendsGroupsOptions || []}
|
||||
onSelect={friends =>
|
||||
handleUpdateFormData({
|
||||
friendsGroups: friends.map(f => String(f.id)),
|
||||
friendsGroups: friends.map(f => f.id),
|
||||
friendsGroupsOptions: friends,
|
||||
})
|
||||
}
|
||||
@@ -385,7 +390,7 @@ const NewAutoLike: React.FC = () => {
|
||||
size="large"
|
||||
loading={isSubmitting}
|
||||
disabled={
|
||||
!formData.friendsgroups || formData.friendsgroups.length === 0
|
||||
!formData.friendsGroups || formData.friendsGroups.length === 0
|
||||
}
|
||||
>
|
||||
{isEditMode ? "更新任务" : "创建任务"}
|
||||
|
||||
@@ -12,8 +12,9 @@ export interface GroupPushTask {
|
||||
createTime: string;
|
||||
creator: string;
|
||||
pushInterval: number;
|
||||
maxPushPerDay: number;
|
||||
timeRange: { start: string; end: string };
|
||||
maxPerDay: number;
|
||||
startTime: string; // 允许推送的开始时间
|
||||
endTime: string; // 允许推送的结束时间
|
||||
messageType: "text" | "image" | "video" | "link";
|
||||
messageContent: string;
|
||||
targetTags: string[];
|
||||
|
||||
@@ -179,7 +179,7 @@ const Detail: React.FC = () => {
|
||||
<div>
|
||||
<SettingOutlined /> <b>基本设置</b>
|
||||
<div>推送间隔:{task.pushInterval} 秒</div>
|
||||
<div>每日最大推送数:{task.maxPushPerDay} 条</div>
|
||||
<div>每日最大推送数:{task.maxPerDay} 条</div>
|
||||
<div>
|
||||
执行时间段:{task.timeRange.start} - {task.timeRange.end}
|
||||
</div>
|
||||
@@ -221,12 +221,10 @@ const Detail: React.FC = () => {
|
||||
<div>
|
||||
<CalendarOutlined /> <b>执行进度</b>
|
||||
<div>
|
||||
今日已推送:{task.pushCount} / {task.maxPushPerDay}
|
||||
今日已推送:{task.pushCount} / {task.maxPerDay}
|
||||
</div>
|
||||
<Progress
|
||||
percent={Math.round(
|
||||
(task.pushCount / task.maxPushPerDay) * 100,
|
||||
)}
|
||||
percent={Math.round((task.pushCount / task.maxPerDay) * 100)}
|
||||
size="small"
|
||||
/>
|
||||
{task.targetTags.length > 0 && (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
@@ -1,8 +1,12 @@
|
||||
import request from "@/api/request";
|
||||
export function createGroupPushTask(taskData) {
|
||||
return request("/v1/workspace/group-push/tasks", taskData, "POST");
|
||||
|
||||
export function createGroupPushTask(data) {
|
||||
return request("/v1/workbench/create", { ...data, type: 3 }, "POST");
|
||||
}
|
||||
|
||||
export function updateGroupPushTask(data) {
|
||||
return request("/v1/workbench/update", { ...data, type: 3 }, "POST");
|
||||
}
|
||||
// 获取京东社交媒体列表
|
||||
export const fetchSocialMediaList = async () => {
|
||||
return request("/v1/workbench/getJdSocialMedia", {}, "GET");
|
||||
|
||||
@@ -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[];
|
||||
// 京东联盟相关字段
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Button } from "antd";
|
||||
import { Toast } from "antd-mobile";
|
||||
import { createGroupPushTask } from "./index.api";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import StepIndicator from "@/components/StepIndicator";
|
||||
@@ -9,7 +10,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 +18,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 +34,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 +50,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,57 +81,61 @@ 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, // 允许推送的结束时间
|
||||
maxPerDay: 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("保存成功");
|
||||
|
||||
// 打印API请求数据,用于调试
|
||||
console.log("发送到API的数据:", apiData);
|
||||
|
||||
// 调用创建或更新 API
|
||||
if (id) {
|
||||
// 更新逻辑将在这里实现
|
||||
Toast.show({ content: "更新成功", position: "top" });
|
||||
navigate("/workspace/group-push");
|
||||
} else {
|
||||
window.alert("保存失败,请稍后重试");
|
||||
createGroupPushTask(apiData)
|
||||
.then(() => {
|
||||
Toast.show({ content: "创建成功", position: "top" });
|
||||
navigate("/workspace/group-push");
|
||||
})
|
||||
.catch(() => {
|
||||
Toast.show({ content: "创建失败,请稍后重试", position: "top" });
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
window.alert("保存失败,请稍后重试");
|
||||
Toast.show({ content: "保存失败,请稍后重试", position: "top" });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -146,7 +148,7 @@ const NewGroupPush: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleNext = async () => {
|
||||
if (currentStep < 4) {
|
||||
if (currentStep < 3) {
|
||||
try {
|
||||
let isValid = false;
|
||||
|
||||
@@ -171,14 +173,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 +190,7 @@ const NewGroupPush: React.FC = () => {
|
||||
上一步
|
||||
</Button>
|
||||
)}
|
||||
{currentStep === 4 ? (
|
||||
{currentStep === 3 ? (
|
||||
<Button size="large" type="primary" onClick={handleSave}>
|
||||
保存
|
||||
</Button>
|
||||
@@ -224,13 +218,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 +247,6 @@ const NewGroupPush: React.FC = () => {
|
||||
onNext={handleLibrariesChange}
|
||||
/>
|
||||
)}
|
||||
{currentStep === 4 && (
|
||||
<JingDongLink ref={jingDongLinkRef} loading={loading} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import request from "@/api/request";
|
||||
import { GroupPushTask } from "../detail/groupPush";
|
||||
|
||||
interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
@@ -11,22 +12,16 @@ export async function fetchGroupPushTasks() {
|
||||
}
|
||||
|
||||
export async function deleteGroupPushTask(id: string): Promise<ApiResponse> {
|
||||
return request(`/v1/workspace/group-push/tasks/${id}`, {}, "DELETE");
|
||||
return request("/v1/workbench/delete", { id }, "DELETE");
|
||||
}
|
||||
|
||||
export async function toggleGroupPushTask(
|
||||
id: string,
|
||||
status: string,
|
||||
): Promise<ApiResponse> {
|
||||
return request(
|
||||
`/v1/workspace/group-push/tasks/${id}/toggle`,
|
||||
{ status },
|
||||
"POST",
|
||||
);
|
||||
// 切换任务状态
|
||||
export function toggleGroupPushTask(data): Promise<any> {
|
||||
return request("/v1/workbench/update-status", { ...data, type: 3 }, "POST");
|
||||
}
|
||||
|
||||
export async function copyGroupPushTask(id: string): Promise<ApiResponse> {
|
||||
return request(`/v1/workspace/group-push/tasks/${id}/copy`, {}, "POST");
|
||||
return request("/v1/workbench/copy", { id }, "POST");
|
||||
}
|
||||
|
||||
export async function createGroupPushTask(
|
||||
|
||||
@@ -133,7 +133,7 @@ const GroupPush: React.FC = () => {
|
||||
const task = tasks.find(t => t.id === taskId);
|
||||
if (!task) return;
|
||||
const newStatus = task.status === 1 ? 2 : 1;
|
||||
await toggleGroupPushTask(taskId, String(newStatus));
|
||||
await toggleGroupPushTask({ id: taskId, status: newStatus });
|
||||
fetchTasks();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user