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

存一波
This commit is contained in:
笔记本里的永平
2025-07-22 10:35:49 +08:00
parent 6e85bc207c
commit 6d2af0a8d9
4 changed files with 570 additions and 9 deletions

View File

@@ -1,8 +0,0 @@
import React from "react";
import PlaceholderPage from "@/components/PlaceholderPage";
const NewMomentsSync: React.FC = () => {
return <PlaceholderPage title="新建朋友圈同步" />;
};
export default NewMomentsSync;

View File

@@ -0,0 +1,243 @@
.formBg {
padding: 16px;
}
.formSteps {
display: flex;
justify-content: center;
margin-bottom: 32px;
gap: 32px;
}
.formStepIndicator {
display: flex;
flex-direction: column;
align-items: center;
color: #bbb;
font-size: 13px;
font-weight: 400;
transition: color 0.2s;
}
.formStepActive {
color: #188eee;
font-weight: 600;
}
.formStepDone {
color: #19c37d;
}
.formStepNum {
width: 28px;
height: 28px;
border-radius: 50%;
background: #e5e7eb;
color: #888;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
margin-bottom: 4px;
}
.formStepActive .formStepNum {
background: #188eee;
color: #fff;
}
.formStepDone .formStepNum {
background: #19c37d;
color: #fff;
}
.formStep {
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
padding: 32px 24px 24px 24px;
width: 100%;
max-width: 420px;
margin: 0 auto 24px auto;
}
.formItem {
margin-bottom: 24px;
}
.formLabel {
font-size: 15px;
color: #222;
font-weight: 500;
margin-bottom: 10px;
}
.input {
height: 44px;
border-radius: 8px;
font-size: 15px;
}
.timeRow {
display: flex;
align-items: center;
}
.inputTime {
width: 90px;
height: 40px;
border-radius: 8px;
font-size: 15px;
}
.timeTo {
margin: 0 8px;
color: #888;
}
.counterRow {
display: flex;
align-items: center;
gap: 0;
}
.counterBtn {
width: 40px;
height: 40px;
border-radius: 8px;
background: #fff;
border: 1px solid #e5e7eb;
font-size: 20px;
color: #188eee;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: border 0.2s;
}
.counterBtn:hover {
border: 1px solid #188eee;
}
.counterValue {
width: 48px;
text-align: center;
font-size: 18px;
font-weight: 600;
color: #222;
}
.counterUnit {
margin-left: 8px;
color: #888;
font-size: 14px;
}
.accountTypeRow {
display: flex;
gap: 12px;
}
.accountTypeBtn {
flex: 1;
height: 44px;
border-radius: 8px;
background: #fff;
border: 1px solid #e5e7eb;
font-size: 15px;
color: #666;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
position: relative;
}
.accountTypeBtn:hover {
border: 1px solid #188eee;
}
.accountTypeActive {
background: #f0f8ff;
border: 1px solid #188eee;
color: #188eee;
}
.questionIcon {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 14px;
color: #999;
}
.switchRow {
display: flex;
align-items: center;
justify-content: space-between;
}
.switchLabel {
font-size: 15px;
color: #222;
font-weight: 500;
}
.switch {
margin-top: 0;
}
.searchInput {
height: 44px;
border-radius: 8px;
font-size: 15px;
}
.searchIcon {
color: #999;
font-size: 16px;
}
.selectedTip {
font-size: 13px;
color: #888;
margin-top: 8px;
}
.formStepBtnRow {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 32px;
}
.prevBtn {
height: 44px;
border-radius: 8px;
font-size: 15px;
min-width: 100px;
}
.nextBtn {
height: 44px;
border-radius: 8px;
font-size: 15px;
min-width: 100px;
}
.completeBtn {
height: 44px;
border-radius: 8px;
font-size: 15px;
min-width: 100px;
}
.formLoading {
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
}

View File

@@ -0,0 +1,326 @@
import React, { useState, useEffect, useCallback } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Button, Input, Switch, message, Spin } from "antd";
import { ArrowLeftOutlined } from "@ant-design/icons";
import { NavBar } from "antd-mobile";
import Layout from "@/components/Layout/Layout";
import style from "./index.module.scss";
import request from "@/api/request";
const steps = ["基础设置", "设备选择", "内容库选择"];
const defaultForm = {
taskName: "",
startTime: "06:00",
endTime: "23:59",
syncCount: 5,
accountType: "business" as "business" | "personal",
enabled: true,
selectedDevices: [] as string[],
selectedLibraries: [] as string[],
};
const NewMomentsSync: React.FC = () => {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
const isEditMode = !!id;
const [currentStep, setCurrentStep] = useState(0);
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState({ ...defaultForm });
// 获取详情(编辑)
const fetchDetail = useCallback(async () => {
if (!id) return;
setLoading(true);
try {
const res = await request("/v1/workbench/detail", { id }, "GET");
if (res) {
setFormData({
taskName: res.name,
startTime: res.timeRange?.start || "06:00",
endTime: res.timeRange?.end || "23:59",
syncCount: res.config?.syncCount || res.syncCount || 5,
accountType: res.accountType === 1 ? "business" : "personal",
enabled: res.status === 1,
selectedDevices: res.config?.devices || [],
selectedLibraries: res.config?.contentLibraryNames || [],
});
}
} catch {
message.error("获取详情失败");
} finally {
setLoading(false);
}
}, [id]);
useEffect(() => {
if (isEditMode) fetchDetail();
}, [isEditMode, fetchDetail]);
// 步骤切换
const next = () => setCurrentStep((s) => Math.min(s + 1, steps.length - 1));
const prev = () => setCurrentStep((s) => Math.max(s - 1, 0));
// 表单数据更新
const updateForm = (data: Partial<typeof formData>) => {
setFormData((prev) => ({ ...prev, ...data }));
};
// 提交
const handleSubmit = async () => {
if (!formData.taskName.trim()) {
message.error("请输入任务名称");
return;
}
if (formData.selectedDevices.length === 0) {
message.error("请选择设备");
return;
}
if (formData.selectedLibraries.length === 0) {
message.error("请选择内容库");
return;
}
setLoading(true);
try {
const params = {
name: formData.taskName,
devices: formData.selectedDevices,
contentLibraries: formData.selectedLibraries,
syncCount: formData.syncCount,
startTime: formData.startTime,
endTime: formData.endTime,
accountType: formData.accountType === "business" ? 1 : 2,
status: formData.enabled ? 1 : 2,
type: 2,
};
if (isEditMode && id) {
await request("/v1/workbench/update", { id, ...params }, "POST");
message.success("更新成功");
navigate(`/workspace/moments-sync/${id}`);
} else {
await request("/v1/workbench/create", params, "POST");
message.success("创建成功");
navigate("/workspace/moments-sync");
}
} catch {
message.error(isEditMode ? "更新失败" : "创建失败");
} finally {
setLoading(false);
}
};
// 步骤内容
const renderStep = () => {
if (currentStep === 0) {
return (
<div className={style.formStep}>
<div className={style.formItem}>
<div className={style.formLabel}></div>
<Input
value={formData.taskName}
onChange={(e) => updateForm({ taskName: e.target.value })}
placeholder="请输入任务名称"
maxLength={30}
className={style.input}
/>
</div>
<div className={style.formItem}>
<div className={style.formLabel}></div>
<div className={style.timeRow}>
<Input
type="time"
value={formData.startTime}
onChange={(e) => updateForm({ startTime: e.target.value })}
className={style.inputTime}
/>
<span className={style.timeTo}></span>
<Input
type="time"
value={formData.endTime}
onChange={(e) => updateForm({ endTime: e.target.value })}
className={style.inputTime}
/>
</div>
</div>
<div className={style.formItem}>
<div className={style.formLabel}></div>
<div className={style.counterRow}>
<button
type="button"
className={style.counterBtn}
onClick={() =>
updateForm({ syncCount: Math.max(1, formData.syncCount - 1) })
}
>
-
</button>
<span className={style.counterValue}>{formData.syncCount}</span>
<button
type="button"
className={style.counterBtn}
onClick={() =>
updateForm({ syncCount: formData.syncCount + 1 })
}
>
+
</button>
<span className={style.counterUnit}></span>
</div>
</div>
<div className={style.formItem}>
<div className={style.formLabel}></div>
<div className={style.accountTypeRow}>
<button
type="button"
className={`${style.accountTypeBtn} ${formData.accountType === "business" ? style.accountTypeActive : ""}`}
onClick={() => updateForm({ accountType: "business" })}
>
</button>
<button
type="button"
className={`${style.accountTypeBtn} ${formData.accountType === "personal" ? style.accountTypeActive : ""}`}
onClick={() => updateForm({ accountType: "personal" })}
>
</button>
</div>
</div>
<div className={style.formItem}>
<div className={style.switchRow}>
<span className={style.switchLabel}></span>
<Switch
checked={formData.enabled}
onChange={(checked) => updateForm({ enabled: checked })}
className={style.switch}
/>
</div>
</div>
<div className={style.formStepBtnRow}>
<Button type="primary" onClick={next} className={style.nextBtn}>
</Button>
</div>
</div>
);
}
if (currentStep === 1) {
return (
<div className={style.formStep}>
<div className={style.formItem}>
<Input
placeholder="选择设备"
prefix={<span className={style.searchIcon}>Q</span>}
className={style.searchInput}
onClick={() => message.info("这里应弹出设备选择器")}
readOnly
/>
{formData.selectedDevices.length > 0 && (
<div className={style.selectedTip}>
: {formData.selectedDevices.length}
</div>
)}
</div>
<div className={style.formStepBtnRow}>
<Button onClick={prev} className={style.prevBtn}>
</Button>
<Button type="primary" onClick={next} className={style.nextBtn}>
</Button>
</div>
</div>
);
}
if (currentStep === 2) {
return (
<div className={style.formStep}>
<div className={style.formItem}>
<Input
placeholder="选择内容库"
prefix={<span className={style.searchIcon}>Q</span>}
className={style.searchInput}
onClick={() => message.info("这里应弹出内容库选择器")}
readOnly
/>
{formData.selectedLibraries.length > 0 && (
<div className={style.selectedTip}>
: {formData.selectedLibraries.length}
</div>
)}
</div>
<div className={style.formStepBtnRow}>
<Button onClick={prev} className={style.prevBtn}>
</Button>
<Button
type="primary"
onClick={handleSubmit}
loading={loading}
className={style.completeBtn}
>
</Button>
</div>
</div>
);
}
return null;
};
return (
<Layout
header={
<NavBar
back={null}
style={{ background: "#fff" }}
left={
<div className="nav-title">
<span className="nav-back-btn">
<ArrowLeftOutlined onClick={() => navigate(-1)} />
</span>
{isEditMode ? "编辑朋友圈同步" : "新建朋友圈同步"}
</div>
}
/>
}
>
<div className={style.formBg}>
<div className={style.formSteps}>
{steps.map((s, i) => (
<div
key={s}
className={
style.formStepIndicator +
" " +
(i === currentStep
? style.formStepActive
: i < currentStep
? style.formStepDone
: "")
}
>
<span className={style.formStepNum}>{i + 1}</span>
<span>{s}</span>
</div>
))}
</div>
{loading ? (
<div className={style.formLoading}>
<Spin />
</div>
) : (
renderStep()
)}
</div>
</Layout>
);
};
export default NewMomentsSync;

View File

@@ -8,7 +8,7 @@ import GroupPush from "@/pages/workspace/group-push/GroupPush";
import NewGroupPush from "@/pages/workspace/group-push/new";
import MomentsSync from "@/pages/workspace/moments-sync/MomentsSync";
import MomentsSyncDetail from "@/pages/workspace/moments-sync/Detail";
import NewMomentsSync from "@/pages/workspace/moments-sync/new";
import NewMomentsSync from "@/pages/workspace/moments-sync/new/index";
import AIAssistant from "@/pages/workspace/ai-assistant/AIAssistant";
import TrafficDistribution from "@/pages/workspace/traffic-distribution/TrafficDistribution";
import TrafficDistributionDetail from "@/pages/workspace/traffic-distribution/Detail";