feat: 本次提交更新内容如下
存一波
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
import React from "react";
|
||||
import PlaceholderPage from "@/components/PlaceholderPage";
|
||||
|
||||
const NewMomentsSync: React.FC = () => {
|
||||
return <PlaceholderPage title="新建朋友圈同步" />;
|
||||
};
|
||||
|
||||
export default NewMomentsSync;
|
||||
243
nkebao/src/pages/workspace/moments-sync/new/index.module.scss
Normal file
243
nkebao/src/pages/workspace/moments-sync/new/index.module.scss
Normal 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;
|
||||
}
|
||||
326
nkebao/src/pages/workspace/moments-sync/new/index.tsx
Normal file
326
nkebao/src/pages/workspace/moments-sync/new/index.tsx
Normal 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;
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user