feat: 本次提交更新内容如下

先存一版作为模板,然后开发其他的
This commit is contained in:
笔记本里的永平
2025-07-23 12:21:47 +08:00
parent d8197be665
commit 55ba56fcf1
6 changed files with 245 additions and 254 deletions

View File

@@ -1,7 +1,7 @@
import request from "@/api/request"; import request from "@/api/request";
// 获取场景类型列表 // 获取场景类型列表
export function getScenarioTypes() { export function getScenarioTypes() {
return request("/v1/scenarios/types", undefined, "GET"); return request("/v1/plan/scenes", undefined, "GET");
} }
// 创建计划 // 创建计划

View File

@@ -1,17 +1,18 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { LeftOutlined } from "@ant-design/icons"; import { message } from "antd";
import { Button, Steps, message } from "antd"; import { NavBar } from "antd-mobile";
import { ArrowLeftOutlined } from "@ant-design/icons";
import BasicSettings from "./steps/BasicSettings"; import BasicSettings from "./steps/BasicSettings";
import FriendRequestSettings from "./steps/FriendRequestSettings"; import FriendRequestSettings from "./steps/FriendRequestSettings";
import MessageSettings from "./steps/MessageSettings"; import MessageSettings from "./steps/MessageSettings";
import Layout from "@/components/Layout/Layout"; import Layout from "@/components/Layout/Layout";
import StepIndicator from "@/components/StepIndicator";
import { import {
getPlanScenes, getScenarioTypes,
createScenarioPlan, createPlan,
fetchPlanDetail, getPlanDetail,
PlanDetail, updatePlan,
updateScenarioPlan,
} from "./index.api"; } from "./index.api";
// 步骤定义 - 只保留三个步骤 // 步骤定义 - 只保留三个步骤
@@ -70,7 +71,7 @@ export default function NewPlan() {
const loadData = async () => { const loadData = async () => {
setSceneLoading(true); setSceneLoading(true);
//获取场景类型 //获取场景类型
getPlanScenes() getScenarioTypes()
.then((data) => { .then((data) => {
setSceneList(data || []); setSceneList(data || []);
}) })
@@ -81,28 +82,25 @@ export default function NewPlan() {
if (planId) { if (planId) {
setIsEdit(true); setIsEdit(true);
//获取计划详情 //获取计划详情
try {
const detail = await fetchPlanDetail(planId); const detail = await getPlanDetail(planId);
setFormData((prev) => ({ setFormData((prev) => ({
...prev, ...prev,
name: detail.name ?? "", name: detail.name ?? "",
scenario: Number(detail.scenario) || 1, scenario: Number(detail.scenario) || 1,
posters: detail.posters ?? [], posters: detail.posters ?? [],
device: detail.device ?? [], device: detail.device ?? [],
remarkType: detail.remarkType ?? "phone", remarkType: detail.remarkType ?? "phone",
greeting: detail.greeting ?? "", greeting: detail.greeting ?? "",
addInterval: detail.addInterval ?? 1, addInterval: detail.addInterval ?? 1,
startTime: detail.startTime ?? "09:00", startTime: detail.startTime ?? "09:00",
endTime: detail.endTime ?? "18:00", endTime: detail.endTime ?? "18:00",
enabled: detail.enabled ?? true, enabled: detail.enabled ?? true,
sceneId: Number(detail.scenario) || 1, sceneId: Number(detail.scenario) || 1,
remarkFormat: detail.remarkFormat ?? "", remarkFormat: detail.remarkFormat ?? "",
addFriendInterval: detail.addFriendInterval ?? 1, addFriendInterval: detail.addFriendInterval ?? 1,
tips: detail.tips ?? "", tips: detail.tips ?? "",
})); }));
} catch (err) {
message.error(err.message || "获取计划详情失败");
}
} else { } else {
if (scenarioId) { if (scenarioId) {
setFormData((prev) => ({ setFormData((prev) => ({
@@ -131,10 +129,10 @@ export default function NewPlan() {
// 兼容后端需要的字段 // 兼容后端需要的字段
// 你可以根据实际需要补充其它字段 // 你可以根据实际需要补充其它字段
}; };
result = await updateScenarioPlan(planId, editData); result = await updatePlan(planId, editData);
} else { } else {
// 新建 // 新建
result = await createScenarioPlan(formData); result = await createPlan(formData);
} }
message.success(isEdit ? "计划已更新" : "获客计划已创建"); message.success(isEdit ? "计划已更新" : "获客计划已创建");
const sceneItem = sceneList.find((v) => formData.scenario === v.id); const sceneItem = sceneList.find((v) => formData.scenario === v.id);
@@ -207,33 +205,29 @@ export default function NewPlan() {
<Layout <Layout
header={ header={
<> <>
<header className="sticky top-0 z-10 bg-white border-b"> <NavBar
<div className="flex items-center justify-between h-14 px-4"> back={null}
<div className="flex items-center"> style={{ background: "#fff" }}
<Button left={
type="text" <div className="nav-title">
shape="circle" <ArrowLeftOutlined
icon={<LeftOutlined />} twoToneColor="#1677ff"
onClick={() => router(-1)} onClick={() => router(-1)}
/> />
</div> </div>
</div> }
</header> >
<span className="nav-title">
{isEdit ? "编辑朋友圈同步" : "新建朋友圈同步"}
</span>
</NavBar>
<div className="px-4 py-6"> <div className="px-4 py-6">
<Steps current={currentStep - 1}> <StepIndicator currentStep={currentStep} steps={steps} />
{steps.map((step, idx) => (
<Steps.Step
key={step.id}
title={step.title}
description={step.subtitle}
/>
))}
</Steps>
</div> </div>
</> </>
} }
> >
<div className="p-4">{renderStepContent()}</div> {renderStepContent()}
</Layout> </Layout>
); );
} }

View File

@@ -1,199 +1,199 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { Button } from "antd-mobile"; import { Button } from "antd-mobile";
import { NavBar } from "antd-mobile"; import { NavBar } from "antd-mobile";
import { ArrowLeftOutlined } from "@ant-design/icons"; import { ArrowLeftOutlined } from "@ant-design/icons";
import { createGroupPushTask } from "@/pages/workspace/group-push/detail/groupPush"; import { createGroupPushTask } from "@/pages/workspace/group-push/detail/groupPush";
import Layout from "@/components/Layout/Layout"; import Layout from "@/components/Layout/Layout";
import StepIndicator from "./components/StepIndicator"; import StepIndicator from "@/components/StepIndicator";
import BasicSettings from "./components/BasicSettings"; import BasicSettings from "./components/BasicSettings";
import GroupSelector from "./components/GroupSelector"; import GroupSelector from "./components/GroupSelector";
import ContentSelector from "./components/ContentSelector"; import ContentSelector from "./components/ContentSelector";
import type { WechatGroup, ContentLibrary, FormData } from "./index.data"; import type { WechatGroup, ContentLibrary, FormData } from "./index.data";
const steps = [ const steps = [
{ id: 1, title: "步骤 1", subtitle: "基础设置" }, { id: 1, title: "步骤 1", subtitle: "基础设置" },
{ id: 2, title: "步骤 2", subtitle: "选择社群" }, { id: 2, title: "步骤 2", subtitle: "选择社群" },
{ id: 3, title: "步骤 3", subtitle: "选择内容库" }, { id: 3, title: "步骤 3", subtitle: "选择内容库" },
{ id: 4, title: "步骤 4", subtitle: "京东联盟" }, { id: 4, title: "步骤 4", subtitle: "京东联盟" },
]; ];
const NewGroupPush: React.FC = () => { const NewGroupPush: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [currentStep, setCurrentStep] = useState(1); const [currentStep, setCurrentStep] = useState(1);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState<FormData>({ const [formData, setFormData] = useState<FormData>({
name: "", name: "",
pushTimeStart: "06:00", pushTimeStart: "06:00",
pushTimeEnd: "23:59", pushTimeEnd: "23:59",
dailyPushCount: 20, dailyPushCount: 20,
pushOrder: "latest", pushOrder: "latest",
isLoopPush: false, isLoopPush: false,
isImmediatePush: false, isImmediatePush: false,
isEnabled: false, isEnabled: false,
groups: [], groups: [],
contentLibraries: [], contentLibraries: [],
}); });
const [isEditMode, setIsEditMode] = useState(false); const [isEditMode, setIsEditMode] = useState(false);
const handleBasicSettingsNext = (values: Partial<FormData>) => { const handleBasicSettingsNext = (values: Partial<FormData>) => {
setFormData((prev) => ({ ...prev, ...values })); setFormData((prev) => ({ ...prev, ...values }));
setCurrentStep(2); setCurrentStep(2);
}; };
const handleGroupsChange = (groups: WechatGroup[]) => { const handleGroupsChange = (groups: WechatGroup[]) => {
setFormData((prev) => ({ ...prev, groups })); setFormData((prev) => ({ ...prev, groups }));
}; };
const handleLibrariesChange = (contentLibraries: ContentLibrary[]) => { const handleLibrariesChange = (contentLibraries: ContentLibrary[]) => {
setFormData((prev) => ({ ...prev, contentLibraries })); setFormData((prev) => ({ ...prev, contentLibraries }));
}; };
const handleSave = async () => { const handleSave = async () => {
if (!formData.name.trim()) { if (!formData.name.trim()) {
window.alert("请输入任务名称"); window.alert("请输入任务名称");
return; return;
} }
if (formData.groups.length === 0) { if (formData.groups.length === 0) {
window.alert("请选择至少一个社群"); window.alert("请选择至少一个社群");
return; return;
} }
if (formData.contentLibraries.length === 0) { if (formData.contentLibraries.length === 0) {
window.alert("请选择至少一个内容库"); window.alert("请选择至少一个内容库");
return; return;
} }
setLoading(true); setLoading(true);
try { try {
const apiData = { const apiData = {
name: formData.name, name: formData.name,
timeRange: { timeRange: {
start: formData.pushTimeStart, start: formData.pushTimeStart,
end: formData.pushTimeEnd, end: formData.pushTimeEnd,
}, },
maxPushPerDay: formData.dailyPushCount, maxPushPerDay: formData.dailyPushCount,
pushOrder: formData.pushOrder, pushOrder: formData.pushOrder,
isLoopPush: formData.isLoopPush, isLoopPush: formData.isLoopPush,
isImmediatePush: formData.isImmediatePush, isImmediatePush: formData.isImmediatePush,
isEnabled: formData.isEnabled, isEnabled: formData.isEnabled,
targetGroups: formData.groups.map((g) => g.name), targetGroups: formData.groups.map((g) => g.name),
contentLibraries: formData.contentLibraries.map((c) => c.name), contentLibraries: formData.contentLibraries.map((c) => c.name),
pushMode: formData.isImmediatePush pushMode: formData.isImmediatePush
? ("immediate" as const) ? ("immediate" as const)
: ("scheduled" as const), : ("scheduled" as const),
messageType: "text" as const, messageType: "text" as const,
messageContent: "", messageContent: "",
targetTags: [], targetTags: [],
pushInterval: 60, pushInterval: 60,
}; };
const response = await createGroupPushTask(apiData); const response = await createGroupPushTask(apiData);
if (response.code === 200) { if (response.code === 200) {
window.alert("保存成功"); window.alert("保存成功");
navigate("/workspace/group-push"); navigate("/workspace/group-push");
} else { } else {
window.alert("保存失败,请稍后重试"); window.alert("保存失败,请稍后重试");
} }
} catch (error) { } catch (error) {
window.alert("保存失败,请稍后重试"); window.alert("保存失败,请稍后重试");
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
const handleCancel = () => { const handleCancel = () => {
navigate("/workspace/group-push"); navigate("/workspace/group-push");
}; };
return ( return (
<Layout <Layout
header={ header={
<NavBar <NavBar
back={null} back={null}
style={{ background: "#fff" }} style={{ background: "#fff" }}
left={ left={
<div className="nav-title"> <div className="nav-title">
<ArrowLeftOutlined <ArrowLeftOutlined
twoToneColor="#1677ff" twoToneColor="#1677ff"
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
/> />
</div> </div>
} }
> >
<span className="nav-title"> <span className="nav-title">
{isEditMode ? "编辑任务" : "新建任务"} {isEditMode ? "编辑任务" : "新建任务"}
</span> </span>
</NavBar> </NavBar>
} }
> >
<div style={{ maxWidth: 600, margin: "0 auto", padding: 16 }}> <div style={{ maxWidth: 600, margin: "0 auto", padding: 16 }}>
<StepIndicator currentStep={currentStep} steps={steps} /> <StepIndicator currentStep={currentStep} steps={steps} />
<div style={{ marginTop: 32 }}> <div style={{ marginTop: 32 }}>
{currentStep === 1 && ( {currentStep === 1 && (
<BasicSettings <BasicSettings
defaultValues={{ defaultValues={{
name: formData.name, name: formData.name,
pushTimeStart: formData.pushTimeStart, pushTimeStart: formData.pushTimeStart,
pushTimeEnd: formData.pushTimeEnd, pushTimeEnd: formData.pushTimeEnd,
dailyPushCount: formData.dailyPushCount, dailyPushCount: formData.dailyPushCount,
pushOrder: formData.pushOrder, pushOrder: formData.pushOrder,
isLoopPush: formData.isLoopPush, isLoopPush: formData.isLoopPush,
isImmediatePush: formData.isImmediatePush, isImmediatePush: formData.isImmediatePush,
isEnabled: formData.isEnabled, isEnabled: formData.isEnabled,
}} }}
onNext={handleBasicSettingsNext} onNext={handleBasicSettingsNext}
onSave={handleSave} onSave={handleSave}
onCancel={handleCancel} onCancel={handleCancel}
loading={loading} loading={loading}
/> />
)} )}
{currentStep === 2 && ( {currentStep === 2 && (
<GroupSelector <GroupSelector
selectedGroups={formData.groups} selectedGroups={formData.groups}
onGroupsChange={handleGroupsChange} onGroupsChange={handleGroupsChange}
onPrevious={() => setCurrentStep(1)} onPrevious={() => setCurrentStep(1)}
onNext={() => setCurrentStep(3)} onNext={() => setCurrentStep(3)}
onSave={handleSave} onSave={handleSave}
onCancel={handleCancel} onCancel={handleCancel}
loading={loading} loading={loading}
/> />
)} )}
{currentStep === 3 && ( {currentStep === 3 && (
<ContentSelector <ContentSelector
selectedLibraries={formData.contentLibraries} selectedLibraries={formData.contentLibraries}
onLibrariesChange={handleLibrariesChange} onLibrariesChange={handleLibrariesChange}
onPrevious={() => setCurrentStep(2)} onPrevious={() => setCurrentStep(2)}
onNext={() => setCurrentStep(4)} onNext={() => setCurrentStep(4)}
onSave={handleSave} onSave={handleSave}
onCancel={handleCancel} onCancel={handleCancel}
loading={loading} loading={loading}
/> />
)} )}
{currentStep === 4 && ( {currentStep === 4 && (
<div style={{ padding: 32, textAlign: "center", color: "#888" }}> <div style={{ padding: 32, textAlign: "center", color: "#888" }}>
<div <div
style={{ style={{
marginTop: 24, marginTop: 24,
display: "flex", display: "flex",
justifyContent: "center", justifyContent: "center",
gap: 8, gap: 8,
}} }}
> >
<Button onClick={() => setCurrentStep(3)} disabled={loading}> <Button onClick={() => setCurrentStep(3)} disabled={loading}>
</Button> </Button>
<Button color="primary" onClick={handleSave} loading={loading}> <Button color="primary" onClick={handleSave} loading={loading}>
</Button> </Button>
<Button onClick={handleCancel} disabled={loading}> <Button onClick={handleCancel} disabled={loading}>
</Button> </Button>
</div> </div>
</div> </div>
)} )}
</div> </div>
</div> </div>
</Layout> </Layout>
); );
}; };
export default NewGroupPush; export default NewGroupPush;

View File

@@ -31,7 +31,6 @@ import {
Menu, Menu,
} from "antd"; } from "antd";
import Layout from "@/components/Layout/Layout"; import Layout from "@/components/Layout/Layout";
import MeauMobile from "@/components/MeauMobile/MeauMoible";
import { import {
fetchGroupPushTasks, fetchGroupPushTasks,
deleteGroupPushTask, deleteGroupPushTask,
@@ -41,8 +40,6 @@ import {
} from "@/pages/workspace/group-push/detail/groupPush"; } from "@/pages/workspace/group-push/detail/groupPush";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
const { Search } = Input;
const GroupPush: React.FC = () => { const GroupPush: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [expandedTaskId, setExpandedTaskId] = useState<string | null>(null); const [expandedTaskId, setExpandedTaskId] = useState<string | null>(null);