feat(流量分发表单): 优化表单管理和数据获取逻辑
重构流量分发表单组件,使用Form管理字段并引入useWatch以简化状态管理。移除未使用的状态和函数,优化了获取详情数据的逻辑。同时,更新了API请求方法以符合RESTful标准,确保删除计划时使用DELETE请求。
This commit is contained in:
@@ -1,15 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
Radio,
|
||||
Slider,
|
||||
TimePicker,
|
||||
message,
|
||||
Checkbox,
|
||||
} from "antd";
|
||||
import { SearchOutlined } from "@ant-design/icons";
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { Form, Input, Button, Radio, Slider, TimePicker, message } from "antd";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import style from "./index.module.scss";
|
||||
import StepIndicator from "@/components/StepIndicator";
|
||||
@@ -24,22 +14,10 @@ import {
|
||||
updateTrafficDistribution,
|
||||
createTrafficDistribution,
|
||||
} from "./api";
|
||||
import type {
|
||||
TrafficDistributionDetail,
|
||||
TrafficDistributionFormData,
|
||||
} from "./data";
|
||||
import type { TrafficDistributionFormData } from "./data";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const scenarioList = [
|
||||
{ label: "海报获客", value: "poster" },
|
||||
{ label: "电话获客", value: "phone" },
|
||||
{ label: "抖音获客", value: "douyin" },
|
||||
{ label: "小红书获客", value: "xiaohongshu" },
|
||||
{ label: "微信群获客", value: "weixinqun" },
|
||||
{ label: "API获客", value: "api" },
|
||||
{ label: "订单获客", value: "order" },
|
||||
{ label: "付款码获客", value: "payment" },
|
||||
];
|
||||
// const scenarioList = [...];
|
||||
const poolList = [
|
||||
{
|
||||
id: "pool-1",
|
||||
@@ -73,10 +51,7 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
const [deviceGroupsOptions, setDeviceGroupsOptions] = useState<any[]>([]);
|
||||
const [accountGroups, setAccountGroups] = useState<any[]>([]);
|
||||
const [accountGroupsOptions, setAccountGroupsOptions] = useState<any[]>([]);
|
||||
const [distributeType, setDistributeType] = useState(1);
|
||||
const [maxPerDay, setMaxPerDay] = useState(50);
|
||||
const [timeType, setTimeType] = useState(1);
|
||||
const [timeRange, setTimeRange] = useState<any>(null);
|
||||
// 使用 Form 管理字段,配合 useWatch 读取值
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
|
||||
@@ -86,9 +61,12 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
"device" | "account"
|
||||
>("device");
|
||||
|
||||
// 编辑时的详情数据
|
||||
const [detailData, setDetailData] =
|
||||
useState<TrafficDistributionDetail | null>(null);
|
||||
// 编辑时的详情数据(不需要保存整份数据,仅回填表单与本地状态)
|
||||
|
||||
// 监听表单字段变化(antd v5 推荐)
|
||||
const maxPerDay = Form.useWatch("maxPerDay", form);
|
||||
const timeType = Form.useWatch("timeType", form);
|
||||
// const timeRange = Form.useWatch("timeRange", form);
|
||||
|
||||
// 生成默认名称
|
||||
const generateDefaultName = () => {
|
||||
@@ -98,20 +76,12 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
return `流量分发 ${dateStr} ${timeStr}`;
|
||||
};
|
||||
|
||||
// 获取详情数据
|
||||
useEffect(() => {
|
||||
if (isEdit && id) {
|
||||
fetchDetail();
|
||||
}
|
||||
}, [isEdit, id]);
|
||||
|
||||
const fetchDetail = async () => {
|
||||
const fetchDetail = useCallback(async () => {
|
||||
if (!id) return;
|
||||
|
||||
setDetailLoading(true);
|
||||
try {
|
||||
const detail = await getTrafficDistributionDetail(id);
|
||||
setDetailData(detail);
|
||||
|
||||
// 回填表单数据
|
||||
const config = detail.config;
|
||||
@@ -122,11 +92,6 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
timeType: config.timeType,
|
||||
});
|
||||
|
||||
// 设置状态
|
||||
setDistributeType(config.distributeType);
|
||||
setMaxPerDay(config.maxPerDay);
|
||||
setTimeType(config.timeType);
|
||||
|
||||
// 设置账号组数据
|
||||
setAccountGroups(config.accountGroups || []);
|
||||
setAccountGroupsOptions(config.accountGroupsOptions || []);
|
||||
@@ -147,7 +112,7 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
// 使用dayjs创建时间对象
|
||||
const startTime = dayjs().hour(startHour).minute(startMinute).second(0);
|
||||
const endTime = dayjs().hour(endHour).minute(endMinute).second(0);
|
||||
setTimeRange([startTime, endTime]);
|
||||
form.setFieldsValue({ timeRange: [startTime, endTime] });
|
||||
}
|
||||
|
||||
// 设置流量池
|
||||
@@ -158,9 +123,21 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
} finally {
|
||||
setDetailLoading(false);
|
||||
}
|
||||
}, [id, form]);
|
||||
|
||||
// 获取详情数据
|
||||
useEffect(() => {
|
||||
if (isEdit && id) {
|
||||
fetchDetail();
|
||||
}
|
||||
}, [isEdit, id, fetchDetail]);
|
||||
const handleFinish = async () => {
|
||||
form.submit();
|
||||
};
|
||||
|
||||
const handleFinish = async (values?: any) => {
|
||||
// 移除未使用的 handleFinish3
|
||||
|
||||
const handleFinish2 = async (values?: any) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 如果没有传递values参数,从表单中获取
|
||||
@@ -173,13 +150,17 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
source: "",
|
||||
sourceIcon: "",
|
||||
description: "",
|
||||
distributeType: distributeType,
|
||||
maxPerDay: maxPerDay,
|
||||
timeType: timeType,
|
||||
distributeType: formValues.distributeType,
|
||||
maxPerDay: formValues.maxPerDay,
|
||||
timeType: formValues.timeType,
|
||||
startTime:
|
||||
timeType === 2 && timeRange?.[0] ? timeRange[0].format("HH:mm") : "",
|
||||
formValues.timeType === 2 && formValues.timeRange?.[0]
|
||||
? formValues.timeRange[0].format("HH:mm")
|
||||
: "",
|
||||
endTime:
|
||||
timeType === 2 && timeRange?.[1] ? timeRange[1].format("HH:mm") : "",
|
||||
formValues.timeType === 2 && formValues.timeRange?.[1]
|
||||
? formValues.timeRange[1].format("HH:mm")
|
||||
: "",
|
||||
deviceGroups: deviceGroups,
|
||||
deviceGroupsOptions: deviceGroupsOptions,
|
||||
accountGroups: accountGroups,
|
||||
@@ -216,6 +197,17 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
.catch(() => {
|
||||
// 验证失败,不进行下一步
|
||||
});
|
||||
} else if (current === 1) {
|
||||
// 第二步:目标设置至少需要选择设备或客服之一
|
||||
const hasDevice =
|
||||
Array.isArray(selectedDevices) && selectedDevices.length > 0;
|
||||
const hasAccount =
|
||||
Array.isArray(selectedAccounts) && selectedAccounts.length > 0;
|
||||
if (!hasDevice && !hasAccount) {
|
||||
message.error("请至少选择一个设备或一个客服");
|
||||
return;
|
||||
}
|
||||
setCurrent(cur => cur + 1);
|
||||
} else {
|
||||
setCurrent(cur => cur + 1);
|
||||
}
|
||||
@@ -225,6 +217,7 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
// 过滤流量池
|
||||
const filteredPools = poolList.filter(pool => pool.name.includes(poolSearch));
|
||||
|
||||
// 移除未使用的输入同步函数
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
@@ -262,104 +255,96 @@ const TrafficDistributionForm: React.FC = () => {
|
||||
>
|
||||
<div className={style.formPage}>
|
||||
<div className={style.formBody}>
|
||||
{current === 0 && (
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
name: isEdit ? "" : generateDefaultName(),
|
||||
distributeType: 1,
|
||||
maxPerDay: 50,
|
||||
timeType: 1,
|
||||
}}
|
||||
disabled={detailLoading}
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
name: isEdit ? "" : generateDefaultName(),
|
||||
distributeType: 1,
|
||||
maxPerDay: 50,
|
||||
timeType: 1,
|
||||
}}
|
||||
disabled={detailLoading}
|
||||
onFinish={handleFinish2}
|
||||
style={{ display: current === 0 ? "block" : "none" }}
|
||||
>
|
||||
<div className={style.sectionTitle}>基本信息</div>
|
||||
<Form.Item
|
||||
label="计划名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: "请输入计划名称" }]}
|
||||
>
|
||||
<div className={style.sectionTitle}>基本信息</div>
|
||||
<Input placeholder="流量分发 20250724 1700" maxLength={30} />
|
||||
</Form.Item>
|
||||
<Form.Item label="分配方式" name="distributeType" required>
|
||||
<Radio.Group className={style.radioGroup}>
|
||||
<Radio value={1}>
|
||||
均分配
|
||||
<span className={style.radioDesc}>
|
||||
(流量将均分分配给所有客服)
|
||||
</span>
|
||||
</Radio>
|
||||
<Radio value={2}>
|
||||
优先级分配
|
||||
<span className={style.radioDesc}>
|
||||
(按客服优先级顺序分配)
|
||||
</span>
|
||||
</Radio>
|
||||
<Radio value={3}>
|
||||
比例分配
|
||||
<span className={style.radioDesc}>(按设置比例分配流量)</span>
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="分配限制" required>
|
||||
<div className={style.sliderLabelWrap}>
|
||||
<span>每日最大分配量</span>
|
||||
<span className={style.sliderValue}>
|
||||
{maxPerDay || 0} 人/天
|
||||
</span>
|
||||
</div>
|
||||
<Form.Item
|
||||
label="计划名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: "请输入计划名称" }]}
|
||||
name="maxPerDay"
|
||||
noStyle
|
||||
rules={[{ required: true, message: "请设置每日最大分配量" }]}
|
||||
>
|
||||
<Input placeholder="流量分发 20250724 1700" maxLength={30} />
|
||||
<Slider min={1} max={100} className={style.slider} />
|
||||
</Form.Item>
|
||||
<Form.Item label="分配方式" name="distributeType" required>
|
||||
<Radio.Group
|
||||
value={distributeType}
|
||||
onChange={e => setDistributeType(e.target.value)}
|
||||
className={style.radioGroup}
|
||||
>
|
||||
<Radio value={1}>
|
||||
均分配
|
||||
<span className={style.radioDesc}>
|
||||
(流量将均分分配给所有客服)
|
||||
</span>
|
||||
</Radio>
|
||||
<Radio value={2}>
|
||||
优先级分配
|
||||
<span className={style.radioDesc}>
|
||||
(按客服优先级顺序分配)
|
||||
</span>
|
||||
</Radio>
|
||||
<Radio value={3}>
|
||||
比例分配
|
||||
<span className={style.radioDesc}>
|
||||
(按设置比例分配流量)
|
||||
</span>
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="分配限制" required>
|
||||
<div className={style.sliderLabelWrap}>
|
||||
<span>每日最大分配量</span>
|
||||
<span className={style.sliderValue}>{maxPerDay} 人/天</span>
|
||||
</div>
|
||||
<Slider
|
||||
min={1}
|
||||
max={100}
|
||||
value={maxPerDay}
|
||||
onChange={setMaxPerDay}
|
||||
className={style.slider}
|
||||
<div className={style.sliderDesc}>限制每天最多分配的流量数量</div>
|
||||
</Form.Item>
|
||||
<Form.Item label="时间限制" name="timeType" required>
|
||||
<Radio.Group className={style.radioGroup}>
|
||||
<Radio value={1}>全天分配</Radio>
|
||||
<Radio value={2}>自定义时间段</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{timeType === 2 && (
|
||||
<Form.Item
|
||||
label=""
|
||||
name="timeRange"
|
||||
required
|
||||
dependencies={["timeType"]}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (getFieldValue("timeType") === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value && value.length === 2) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(new Error("请选择开始和结束时间"));
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<TimePicker.RangePicker
|
||||
format="HH:mm"
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
<div className={style.sliderDesc}>
|
||||
限制每天最多分配的流量数量
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label="时间限制" name="timeType" required>
|
||||
<Radio.Group
|
||||
value={timeType}
|
||||
onChange={e => setTimeType(e.target.value)}
|
||||
className={style.radioGroup}
|
||||
>
|
||||
<Radio value={1}>全天分配</Radio>
|
||||
<Radio value={2}>自定义时间段</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{timeType === 2 && (
|
||||
<Form.Item label="" required>
|
||||
<div className={style.timeRangeWrap}>
|
||||
<div>
|
||||
<span>开始时间</span>
|
||||
<TimePicker
|
||||
format="HH:mm"
|
||||
style={{ width: 120 }}
|
||||
value={timeRange?.[0]}
|
||||
onChange={v => setTimeRange([v, timeRange?.[1]])}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>结束时间</span>
|
||||
<TimePicker
|
||||
format="HH:mm"
|
||||
style={{ width: 120 }}
|
||||
value={timeRange?.[1]}
|
||||
onChange={v => setTimeRange([timeRange?.[0], v])}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
)}
|
||||
</Form>
|
||||
{current === 1 && (
|
||||
<div>
|
||||
<div className={style.sectionTitle}>目标设置</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ export function toggleDistributionRuleStatus(
|
||||
|
||||
// 删除计划
|
||||
export function deleteDistributionRule(id: number): Promise<any> {
|
||||
return request("/v1/workbench/delete", { id }, "POST");
|
||||
return request("/v1/workbench/delete", { id }, "DELETE");
|
||||
}
|
||||
|
||||
// 获取流量分发规则详情
|
||||
|
||||
Reference in New Issue
Block a user