feat: 本次提交更新内容如下
暂存一下
This commit is contained in:
@@ -1,371 +1,371 @@
|
|||||||
import React, { useEffect, useState, useCallback, useRef } from "react";
|
import React, { useEffect, useState, useCallback, useRef } from "react";
|
||||||
import { useParams, useNavigate } from "react-router-dom";
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
import { NavBar, Tabs, Switch, Toast, SpinLoading, Button } from "antd-mobile";
|
import { NavBar, Tabs, Switch, Toast, SpinLoading, Button } from "antd-mobile";
|
||||||
import { SettingOutlined, RedoOutlined } from "@ant-design/icons";
|
import { SettingOutlined, RedoOutlined } from "@ant-design/icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import {
|
import {
|
||||||
fetchDeviceDetail,
|
fetchDeviceDetail,
|
||||||
fetchDeviceRelatedAccounts,
|
fetchDeviceRelatedAccounts,
|
||||||
fetchDeviceHandleLogs,
|
fetchDeviceHandleLogs,
|
||||||
updateDeviceTaskConfig,
|
updateDeviceTaskConfig,
|
||||||
} from "@/api/devices";
|
} from "@/api/devices";
|
||||||
import type { Device, WechatAccount, HandleLog } from "@/types/device";
|
import type { Device, WechatAccount, HandleLog } from "@/types/device";
|
||||||
|
|
||||||
const DeviceDetail: React.FC = () => {
|
const DeviceDetail: React.FC = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [device, setDevice] = useState<Device | null>(null);
|
const [device, setDevice] = useState<Device | null>(null);
|
||||||
const [tab, setTab] = useState("info");
|
const [tab, setTab] = useState("info");
|
||||||
const [accounts, setAccounts] = useState<WechatAccount[]>([]);
|
const [accounts, setAccounts] = useState<WechatAccount[]>([]);
|
||||||
const [accountsLoading, setAccountsLoading] = useState(false);
|
const [accountsLoading, setAccountsLoading] = useState(false);
|
||||||
const [logs, setLogs] = useState<HandleLog[]>([]);
|
const [logs, setLogs] = useState<HandleLog[]>([]);
|
||||||
const [logsLoading, setLogsLoading] = useState(false);
|
const [logsLoading, setLogsLoading] = useState(false);
|
||||||
const [featureSaving, setFeatureSaving] = useState<{ [k: string]: boolean }>(
|
const [featureSaving, setFeatureSaving] = useState<{ [k: string]: boolean }>(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 获取设备详情
|
// 获取设备详情
|
||||||
const loadDetail = useCallback(async () => {
|
const loadDetail = useCallback(async () => {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetchDeviceDetail(id);
|
const res = await fetchDeviceDetail(id);
|
||||||
setDevice(res);
|
setDevice(res);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Toast.show({ content: e.message || "获取设备详情失败", position: "top" });
|
Toast.show({ content: e.message || "获取设备详情失败", position: "top" });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
// 获取关联账号
|
// 获取关联账号
|
||||||
const loadAccounts = useCallback(async () => {
|
const loadAccounts = useCallback(async () => {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
setAccountsLoading(true);
|
setAccountsLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetchDeviceRelatedAccounts(id);
|
const res = await fetchDeviceRelatedAccounts(id);
|
||||||
setAccounts(Array.isArray(res.accounts) ? res.accounts : []);
|
setAccounts(Array.isArray(res.accounts) ? res.accounts : []);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Toast.show({ content: e.message || "获取关联账号失败", position: "top" });
|
Toast.show({ content: e.message || "获取关联账号失败", position: "top" });
|
||||||
} finally {
|
} finally {
|
||||||
setAccountsLoading(false);
|
setAccountsLoading(false);
|
||||||
}
|
}
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
// 获取操作日志
|
// 获取操作日志
|
||||||
const loadLogs = useCallback(async () => {
|
const loadLogs = useCallback(async () => {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
setLogsLoading(true);
|
setLogsLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetchDeviceHandleLogs(id, 1, 20);
|
const res = await fetchDeviceHandleLogs(id, 1, 20);
|
||||||
setLogs(Array.isArray(res.list) ? res.list : []);
|
setLogs(Array.isArray(res.list) ? res.list : []);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Toast.show({ content: e.message || "获取操作日志失败", position: "top" });
|
Toast.show({ content: e.message || "获取操作日志失败", position: "top" });
|
||||||
} finally {
|
} finally {
|
||||||
setLogsLoading(false);
|
setLogsLoading(false);
|
||||||
}
|
}
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadDetail();
|
loadDetail();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tab === "accounts") loadAccounts();
|
if (tab === "accounts") loadAccounts();
|
||||||
if (tab === "logs") loadLogs();
|
if (tab === "logs") loadLogs();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [tab]);
|
}, [tab]);
|
||||||
|
|
||||||
// 功能开关
|
// 功能开关
|
||||||
const handleFeatureChange = async (
|
const handleFeatureChange = async (
|
||||||
feature: keyof Device["features"],
|
feature: keyof Device["features"],
|
||||||
checked: boolean
|
checked: boolean
|
||||||
) => {
|
) => {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
setFeatureSaving((prev) => ({ ...prev, [feature]: true }));
|
setFeatureSaving((prev) => ({ ...prev, [feature]: true }));
|
||||||
try {
|
try {
|
||||||
await updateDeviceTaskConfig({ deviceId: id, [feature]: checked });
|
await updateDeviceTaskConfig({ deviceId: id, [feature]: checked });
|
||||||
setDevice((prev) =>
|
setDevice((prev) =>
|
||||||
prev
|
prev
|
||||||
? {
|
? {
|
||||||
...prev,
|
...prev,
|
||||||
features: { ...prev.features, [feature]: checked },
|
features: { ...prev.features, [feature]: checked },
|
||||||
}
|
}
|
||||||
: prev
|
: prev
|
||||||
);
|
);
|
||||||
Toast.show({
|
Toast.show({
|
||||||
content: `${getFeatureName(feature)}已${checked ? "开启" : "关闭"}`,
|
content: `${getFeatureName(feature)}已${checked ? "开启" : "关闭"}`,
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Toast.show({ content: e.message || "设置失败", position: "top" });
|
Toast.show({ content: e.message || "设置失败", position: "top" });
|
||||||
} finally {
|
} finally {
|
||||||
setFeatureSaving((prev) => ({ ...prev, [feature]: false }));
|
setFeatureSaving((prev) => ({ ...prev, [feature]: false }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFeatureName = (feature: string) => {
|
const getFeatureName = (feature: string) => {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
autoAddFriend: "自动加好友",
|
autoAddFriend: "自动加好友",
|
||||||
autoReply: "自动回复",
|
autoReply: "自动回复",
|
||||||
momentsSync: "朋友圈同步",
|
momentsSync: "朋友圈同步",
|
||||||
aiChat: "AI会话",
|
aiChat: "AI会话",
|
||||||
};
|
};
|
||||||
return map[feature] || feature;
|
return map[feature] || feature;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
header={
|
header={
|
||||||
<NavBar
|
<NavBar
|
||||||
onBack={() => navigate(-1)}
|
onBack={() => navigate(-1)}
|
||||||
style={{ background: "#fff" }}
|
style={{ background: "#fff" }}
|
||||||
right={
|
right={
|
||||||
<Button size="small" color="primary">
|
<Button size="small" color="primary">
|
||||||
<SettingOutlined />
|
<SettingOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span style={{ color: "var(--primary-color)", fontWeight: 600 }}>
|
<span style={{ color: "var(--primary-color)", fontWeight: 600 }}>
|
||||||
设备详情
|
设备详情
|
||||||
</span>
|
</span>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
}
|
}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
>
|
>
|
||||||
{!device ? (
|
{!device ? (
|
||||||
<div style={{ padding: 32, textAlign: "center", color: "#888" }}>
|
<div style={{ padding: 32, textAlign: "center", color: "#888" }}>
|
||||||
<SpinLoading style={{ "--size": "32px" }} />
|
<SpinLoading style={{ "--size": "32px" }} />
|
||||||
<div style={{ marginTop: 16 }}>正在加载设备信息...</div>
|
<div style={{ marginTop: 16 }}>正在加载设备信息...</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ padding: 12 }}>
|
<div style={{ padding: 12 }}>
|
||||||
{/* 基本信息卡片 */}
|
{/* 基本信息卡片 */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: "#fff",
|
background: "#fff",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
boxShadow: "0 1px 4px #eee",
|
boxShadow: "0 1px 4px #eee",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ fontWeight: 600, fontSize: 18 }}>
|
<div style={{ fontWeight: 600, fontSize: 18 }}>
|
||||||
{device.memo || "未命名设备"}
|
{device.memo || "未命名设备"}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 13, color: "#888", marginTop: 4 }}>
|
<div style={{ fontSize: 13, color: "#888", marginTop: 4 }}>
|
||||||
IMEI: {device.imei}
|
IMEI: {device.imei}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 13, color: "#888", marginTop: 4 }}>
|
<div style={{ fontSize: 13, color: "#888", marginTop: 4 }}>
|
||||||
微信号: {device.wechatId || "未绑定"}
|
微信号: {device.wechatId || "未绑定"}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 13, color: "#888", marginTop: 4 }}>
|
<div style={{ fontSize: 13, color: "#888", marginTop: 4 }}>
|
||||||
好友数: {device.totalFriend ?? "-"}
|
好友数: {device.totalFriend ?? "-"}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color:
|
color:
|
||||||
device.status === "online" || device.alive === 1
|
device.status === "online" || device.alive === 1
|
||||||
? "#52c41a"
|
? "#52c41a"
|
||||||
: "#aaa",
|
: "#aaa",
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{device.status === "online" || device.alive === 1
|
{device.status === "online" || device.alive === 1
|
||||||
? "在线"
|
? "在线"
|
||||||
: "离线"}
|
: "离线"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 标签页 */}
|
{/* 标签页 */}
|
||||||
<Tabs activeKey={tab} onChange={setTab} style={{ marginBottom: 12 }}>
|
<Tabs activeKey={tab} onChange={setTab} style={{ marginBottom: 12 }}>
|
||||||
<Tabs.Tab title="功能开关" key="info" />
|
<Tabs.Tab title="功能开关" key="info" />
|
||||||
<Tabs.Tab title="关联账号" key="accounts" />
|
<Tabs.Tab title="关联账号" key="accounts" />
|
||||||
<Tabs.Tab title="操作日志" key="logs" />
|
<Tabs.Tab title="操作日志" key="logs" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{/* 功能开关 */}
|
{/* 功能开关 */}
|
||||||
{tab === "info" && (
|
{tab === "info" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: "#fff",
|
background: "#fff",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
boxShadow: "0 1px 4px #eee",
|
boxShadow: "0 1px 4px #eee",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 18,
|
gap: 18,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{["autoAddFriend", "autoReply", "momentsSync", "aiChat"].map(
|
{["autoAddFriend", "autoReply", "momentsSync", "aiChat"].map(
|
||||||
(f) => (
|
(f) => (
|
||||||
<div
|
<div
|
||||||
key={f}
|
key={f}
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontWeight: 500 }}>{getFeatureName(f)}</div>
|
<div style={{ fontWeight: 500 }}>{getFeatureName(f)}</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={
|
checked={
|
||||||
!!device.features?.[f as keyof Device["features"]]
|
!!device.features?.[f as keyof Device["features"]]
|
||||||
}
|
}
|
||||||
loading={!!featureSaving[f]}
|
loading={!!featureSaving[f]}
|
||||||
onChange={(checked) =>
|
onChange={(checked) =>
|
||||||
handleFeatureChange(
|
handleFeatureChange(
|
||||||
f as keyof Device["features"],
|
f as keyof Device["features"],
|
||||||
checked
|
checked
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* 关联账号 */}
|
{/* 关联账号 */}
|
||||||
{tab === "accounts" && (
|
{tab === "accounts" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: "#fff",
|
background: "#fff",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
boxShadow: "0 1px 4px #eee",
|
boxShadow: "0 1px 4px #eee",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{accountsLoading ? (
|
{accountsLoading ? (
|
||||||
<div
|
<div
|
||||||
style={{ textAlign: "center", color: "#888", padding: 32 }}
|
style={{ textAlign: "center", color: "#888", padding: 32 }}
|
||||||
>
|
>
|
||||||
<SpinLoading />
|
<SpinLoading />
|
||||||
</div>
|
</div>
|
||||||
) : accounts.length === 0 ? (
|
) : accounts.length === 0 ? (
|
||||||
<div
|
<div
|
||||||
style={{ textAlign: "center", color: "#aaa", padding: 32 }}
|
style={{ textAlign: "center", color: "#aaa", padding: 32 }}
|
||||||
>
|
>
|
||||||
暂无关联微信账号
|
暂无关联微信账号
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{ display: "flex", flexDirection: "column", gap: 12 }}
|
style={{ display: "flex", flexDirection: "column", gap: 12 }}
|
||||||
>
|
>
|
||||||
{accounts.map((acc) => (
|
{accounts.map((acc) => (
|
||||||
<div
|
<div
|
||||||
key={acc.id}
|
key={acc.id}
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 12,
|
gap: 12,
|
||||||
background: "#f7f8fa",
|
background: "#f7f8fa",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={acc.avatar || "/placeholder.svg"}
|
src={acc.avatar || "/placeholder.svg"}
|
||||||
alt={acc.nickname}
|
alt={acc.nickname}
|
||||||
style={{
|
style={{
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
background: "#eee",
|
background: "#eee",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<div style={{ fontWeight: 500 }}>{acc.nickname}</div>
|
<div style={{ fontWeight: 500 }}>{acc.nickname}</div>
|
||||||
<div style={{ fontSize: 12, color: "#888" }}>
|
<div style={{ fontSize: 12, color: "#888" }}>
|
||||||
微信号: {acc.wechatId}
|
微信号: {acc.wechatId}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: "#888" }}>
|
<div style={{ fontSize: 12, color: "#888" }}>
|
||||||
好友数: {acc.totalFriend}
|
好友数: {acc.totalFriend}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: "#aaa" }}>
|
<div style={{ fontSize: 12, color: "#aaa" }}>
|
||||||
最后活跃: {acc.lastActive}
|
最后活跃: {acc.lastActive}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: acc.wechatAlive === 1 ? "#52c41a" : "#aaa",
|
color: acc.wechatAlive === 1 ? "#52c41a" : "#aaa",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{acc.wechatAliveText}
|
{acc.wechatAliveText}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ textAlign: "center", marginTop: 16 }}>
|
<div style={{ textAlign: "center", marginTop: 16 }}>
|
||||||
<Button size="small" onClick={loadAccounts}>
|
<Button size="small" onClick={loadAccounts}>
|
||||||
<RedoOutlined />
|
<RedoOutlined />
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* 操作日志 */}
|
{/* 操作日志 */}
|
||||||
{tab === "logs" && (
|
{tab === "logs" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: "#fff",
|
background: "#fff",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
boxShadow: "0 1px 4px #eee",
|
boxShadow: "0 1px 4px #eee",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{logsLoading ? (
|
{logsLoading ? (
|
||||||
<div
|
<div
|
||||||
style={{ textAlign: "center", color: "#888", padding: 32 }}
|
style={{ textAlign: "center", color: "#888", padding: 32 }}
|
||||||
>
|
>
|
||||||
<SpinLoading />
|
<SpinLoading />
|
||||||
</div>
|
</div>
|
||||||
) : logs.length === 0 ? (
|
) : logs.length === 0 ? (
|
||||||
<div
|
<div
|
||||||
style={{ textAlign: "center", color: "#aaa", padding: 32 }}
|
style={{ textAlign: "center", color: "#aaa", padding: 32 }}
|
||||||
>
|
>
|
||||||
暂无操作日志
|
暂无操作日志
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{ display: "flex", flexDirection: "column", gap: 12 }}
|
style={{ display: "flex", flexDirection: "column", gap: 12 }}
|
||||||
>
|
>
|
||||||
{logs.map((log) => (
|
{logs.map((log) => (
|
||||||
<div
|
<div
|
||||||
key={log.id}
|
key={log.id}
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 2,
|
gap: 2,
|
||||||
background: "#f7f8fa",
|
background: "#f7f8fa",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ fontWeight: 500 }}>{log.content}</div>
|
<div style={{ fontWeight: 500 }}>{log.content}</div>
|
||||||
<div style={{ fontSize: 12, color: "#888" }}>
|
<div style={{ fontSize: 12, color: "#888" }}>
|
||||||
操作人: {log.username} · {log.createTime}
|
操作人: {log.username} · {log.createTime}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ textAlign: "center", marginTop: 16 }}>
|
<div style={{ textAlign: "center", marginTop: 16 }}>
|
||||||
<Button size="small" onClick={loadLogs}>
|
<Button size="small" onClick={loadLogs}>
|
||||||
<RedoOutlined />
|
<RedoOutlined />
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeviceDetail;
|
export default DeviceDetail;
|
||||||
|
|||||||
@@ -1,286 +1,294 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Switch, Input, message, Dropdown, Menu } from "antd";
|
import { Switch, Input, message, Dropdown, Menu } from "antd";
|
||||||
import { NavBar, Button } from "antd-mobile";
|
import { NavBar, Button } from "antd-mobile";
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
EyeOutlined,
|
EyeOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
CopyOutlined,
|
CopyOutlined,
|
||||||
MoreOutlined,
|
MoreOutlined,
|
||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
ArrowLeftOutlined,
|
ArrowLeftOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import style from "./index.module.scss";
|
import style from "./index.module.scss";
|
||||||
import request from "@/api/request";
|
import request from "@/api/request";
|
||||||
|
|
||||||
interface MomentsSyncTask {
|
interface MomentsSyncTask {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
status: 1 | 2;
|
status: 1 | 2;
|
||||||
deviceCount: number;
|
deviceCount: number;
|
||||||
syncCount: number;
|
syncCount: number;
|
||||||
lastSyncTime: string;
|
lastSyncTime: string;
|
||||||
createTime: string;
|
createTime: string;
|
||||||
creatorName: string;
|
creatorName: string;
|
||||||
contentLib?: string;
|
contentLib?: string;
|
||||||
config?: { devices?: string[]; contentLibraryNames?: string[] };
|
config?: { devices?: string[]; contentLibraryNames?: string[] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusText = (status: number) => {
|
const getStatusText = (status: number) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return "进行中";
|
return "进行中";
|
||||||
case 2:
|
case 2:
|
||||||
return "已暂停";
|
return "已暂停";
|
||||||
default:
|
default:
|
||||||
return "未知";
|
return "未知";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const MomentsSync: React.FC = () => {
|
const MomentsSync: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [tasks, setTasks] = useState<MomentsSyncTask[]>([]);
|
const [tasks, setTasks] = useState<MomentsSyncTask[]>([]);
|
||||||
|
|
||||||
const fetchTasks = async () => {
|
const fetchTasks = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await request(
|
const res = await request(
|
||||||
"/v1/workbench/list",
|
"/v1/workbench/list",
|
||||||
{ type: 2, page: 1, limit: 100 },
|
{ type: 2, page: 1, limit: 100 },
|
||||||
"GET"
|
"GET"
|
||||||
);
|
);
|
||||||
setTasks(res.list || []);
|
setTasks(res.list || []);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message.error("获取任务失败");
|
message.error("获取任务失败");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDelete = async (id: string) => {
|
const handleDelete = async (id: string) => {
|
||||||
if (!window.confirm("确定要删除该任务吗?")) return;
|
if (!window.confirm("确定要删除该任务吗?")) return;
|
||||||
try {
|
try {
|
||||||
await request("/v1/workbench/delete", { id }, "DELETE");
|
await request("/v1/workbench/delete", { id }, "DELETE");
|
||||||
message.success("删除成功");
|
message.success("删除成功");
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
} catch {
|
} catch {
|
||||||
message.error("删除失败");
|
message.error("删除失败");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = async (id: string) => {
|
const handleCopy = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
await request("/v1/workbench/copy", { id }, "POST");
|
await request("/v1/workbench/copy", { id }, "POST");
|
||||||
message.success("复制成功");
|
message.success("复制成功");
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
} catch {
|
} catch {
|
||||||
message.error("复制失败");
|
message.error("复制失败");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggle = async (id: string, status: number) => {
|
const handleToggle = async (id: string, status: number) => {
|
||||||
const newStatus = status === 1 ? 2 : 1;
|
const newStatus = status === 1 ? 2 : 1;
|
||||||
try {
|
try {
|
||||||
await request(
|
await request(
|
||||||
"/v1/workbench/update-status",
|
"/v1/workbench/update-status",
|
||||||
{ id, status: newStatus },
|
{ id, status: newStatus },
|
||||||
"POST"
|
"POST"
|
||||||
);
|
);
|
||||||
setTasks((prev) =>
|
setTasks((prev) =>
|
||||||
prev.map((t) => (t.id === id ? { ...t, status: newStatus } : t))
|
prev.map((t) => (t.id === id ? { ...t, status: newStatus } : t))
|
||||||
);
|
);
|
||||||
message.success("操作成功");
|
message.success("操作成功");
|
||||||
} catch {
|
} catch {
|
||||||
message.error("操作失败");
|
message.error("操作失败");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredTasks = tasks.filter((task) =>
|
const filteredTasks = tasks.filter((task) =>
|
||||||
task.name.toLowerCase().includes(searchTerm.toLowerCase())
|
task.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
// 菜单
|
// 菜单
|
||||||
const getMenu = (task: MomentsSyncTask) => (
|
const getMenu = (task: MomentsSyncTask) => (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="view"
|
key="view"
|
||||||
icon={<EyeOutlined />}
|
icon={<EyeOutlined />}
|
||||||
onClick={() => navigate(`/workspace/moments-sync/${task.id}`)}
|
onClick={() => navigate(`/workspace/moments-sync/${task.id}`)}
|
||||||
>
|
>
|
||||||
查看
|
查看
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="edit"
|
key="edit"
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => navigate(`/workspace/moments-sync/edit/${task.id}`)}
|
onClick={() => navigate(`/workspace/moments-sync/edit/${task.id}`)}
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="copy"
|
key="copy"
|
||||||
icon={<CopyOutlined />}
|
icon={<CopyOutlined />}
|
||||||
onClick={() => handleCopy(task.id)}
|
onClick={() => handleCopy(task.id)}
|
||||||
>
|
>
|
||||||
复制
|
复制
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="delete"
|
key="delete"
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
onClick={() => handleDelete(task.id)}
|
onClick={() => handleDelete(task.id)}
|
||||||
danger
|
danger
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
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("/workspace")}
|
onClick={() => navigate("/workspace")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => navigate("/workspace/moments-sync/new")}
|
onClick={() => navigate("/workspace/moments-sync/new")}
|
||||||
>
|
>
|
||||||
<PlusOutlined /> 新建任务
|
<PlusOutlined /> 新建任务
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="nav-title">朋友圈同步</span>
|
<span className="nav-title">朋友圈同步</span>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<div className="search-bar">
|
<div className="search-bar">
|
||||||
<div className="search-input-wrapper">
|
<div className="search-input-wrapper">
|
||||||
<Input
|
<Input
|
||||||
placeholder="搜索任务名称"
|
placeholder="搜索任务名称"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
prefix={<SearchOutlined />}
|
prefix={<SearchOutlined />}
|
||||||
allowClear
|
allowClear
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
onClick={fetchTasks}
|
onClick={fetchTasks}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
className="refresh-btn"
|
className="refresh-btn"
|
||||||
>
|
>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className={style.pageBg}>
|
<div className={style.pageBg}>
|
||||||
<div className={style.taskList}>
|
<div className={style.taskList}>
|
||||||
{filteredTasks.length === 0 ? (
|
{filteredTasks.length === 0 ? (
|
||||||
<div className={style.emptyBox}>
|
<div className={style.emptyBox}>
|
||||||
<span style={{ fontSize: 40, color: "#ddd" }}>
|
<span style={{ fontSize: 40, color: "#ddd" }}>
|
||||||
<ClockCircleOutlined />
|
<ClockCircleOutlined />
|
||||||
</span>
|
</span>
|
||||||
<div className={style.emptyText}>暂无同步任务</div>
|
<div className={style.emptyText}>暂无同步任务</div>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => navigate("/workspace/moments-sync/new")}
|
onClick={() => navigate("/workspace/moments-sync/new")}
|
||||||
>
|
>
|
||||||
新建第一个任务
|
新建第一个任务
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filteredTasks.map((task) => (
|
filteredTasks.map((task) => (
|
||||||
<div key={task.id} className={style.itemCard}>
|
<div key={task.id} className={style.itemCard}>
|
||||||
<div className={style.itemTop}>
|
<div className={style.itemTop}>
|
||||||
<div className={style.itemTitle}>
|
<div className={style.itemTitle}>
|
||||||
<span className={style.itemName}>{task.name}</span>
|
<span className={style.itemName}>{task.name}</span>
|
||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
task.status === 1
|
task.status === 1
|
||||||
? style.statusPill + " " + style.statusActive
|
? style.statusPill + " " + style.statusActive
|
||||||
: style.statusPill + " " + style.statusPaused
|
: style.statusPill + " " + style.statusPaused
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{getStatusText(task.status)}
|
{getStatusText(task.status)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.itemActions}>
|
<div className={style.itemActions}>
|
||||||
<Switch
|
<Switch
|
||||||
checked={task.status === 1}
|
checked={task.status === 1}
|
||||||
onChange={() => handleToggle(task.id, task.status)}
|
onChange={() => handleToggle(task.id, task.status)}
|
||||||
className={style.switchBtn}
|
className={style.switchBtn}
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
overlay={getMenu(task)}
|
overlay={getMenu(task)}
|
||||||
trigger={["click"]}
|
trigger={["click"]}
|
||||||
placement="bottomRight"
|
placement="bottomRight"
|
||||||
>
|
>
|
||||||
<Button
|
<button
|
||||||
type="text"
|
className={style.moreBtn}
|
||||||
icon={<MoreOutlined />}
|
style={{
|
||||||
className={style.moreBtn}
|
background: "none",
|
||||||
/>
|
border: "none",
|
||||||
</Dropdown>
|
padding: 0,
|
||||||
</div>
|
cursor: "pointer",
|
||||||
</div>
|
}}
|
||||||
<div className={style.itemInfoRow}>
|
tabIndex={0}
|
||||||
<div className={style.infoCol}>
|
aria-label="更多操作"
|
||||||
推送设备:{task.config?.devices?.length || 0} 个
|
>
|
||||||
</div>
|
<MoreOutlined />
|
||||||
<div className={style.infoCol}>
|
</button>
|
||||||
已同步:{task.syncCount || 0} 条
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.itemInfoRow}>
|
<div className={style.itemInfoRow}>
|
||||||
<div className={style.infoCol}>
|
<div className={style.infoCol}>
|
||||||
内容库:
|
推送设备:{task.config?.devices?.length || 0} 个
|
||||||
{task.config?.contentLibraryNames?.join(",") ||
|
</div>
|
||||||
task.contentLib ||
|
<div className={style.infoCol}>
|
||||||
"默认内容库"}
|
已同步:{task.syncCount || 0} 条
|
||||||
</div>
|
</div>
|
||||||
<div className={style.infoCol}>
|
</div>
|
||||||
创建人:{task.creatorName}
|
<div className={style.itemInfoRow}>
|
||||||
</div>
|
<div className={style.infoCol}>
|
||||||
</div>
|
内容库:
|
||||||
<div className={style.itemBottom}>
|
{task.config?.contentLibraryNames?.join(",") ||
|
||||||
<div className={style.bottomLeft}>
|
task.contentLib ||
|
||||||
<ClockCircleOutlined className={style.clockIcon} />
|
"默认内容库"}
|
||||||
上次同步:{task.lastSyncTime || "无"}
|
</div>
|
||||||
</div>
|
<div className={style.infoCol}>
|
||||||
<div className={style.bottomRight}>
|
创建人:{task.creatorName}
|
||||||
创建时间:{task.createTime}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className={style.itemBottom}>
|
||||||
</div>
|
<div className={style.bottomLeft}>
|
||||||
))
|
<ClockCircleOutlined className={style.clockIcon} />
|
||||||
)}
|
上次同步:{task.lastSyncTime || "无"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className={style.bottomRight}>
|
||||||
</Layout>
|
创建时间:{task.createTime}
|
||||||
);
|
</div>
|
||||||
};
|
</div>
|
||||||
|
</div>
|
||||||
export default MomentsSync;
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MomentsSync;
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
.formBg {
|
.formBg {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
.formStepBtnRow{
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 32px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
.formSteps {
|
.formSteps {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { Button, Input, Switch, message, Spin } from "antd";
|
import { Button, Input, Switch, message, Spin } from "antd";
|
||||||
import { ArrowLeftOutlined } from "@ant-design/icons";
|
import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
import { NavBar } from "antd-mobile";
|
|
||||||
|
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import style from "./index.module.scss";
|
import style from "./index.module.scss";
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
} from "./api";
|
} from "./api";
|
||||||
import DeviceSelection from "@/components/DeviceSelection";
|
import DeviceSelection from "@/components/DeviceSelection";
|
||||||
import ContentLibrarySelection from "@/components/ContentLibrarySelection";
|
import ContentLibrarySelection from "@/components/ContentLibrarySelection";
|
||||||
|
import NavCommon from "@/components/NavCommon";
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{ id: 1, title: "基础设置", subtitle: "基础设置" },
|
{ id: 1, title: "基础设置", subtitle: "基础设置" },
|
||||||
@@ -27,10 +27,15 @@ const defaultForm = {
|
|||||||
startTime: "06:00",
|
startTime: "06:00",
|
||||||
endTime: "23:59",
|
endTime: "23:59",
|
||||||
syncCount: 5,
|
syncCount: 5,
|
||||||
accountType: "business" as "business" | "personal",
|
syncInterval: 30,
|
||||||
|
syncType: 1, // 1=业务号 2=人设号
|
||||||
|
accountType: "business" as "business" | "personal", // 仅UI用
|
||||||
enabled: true,
|
enabled: true,
|
||||||
selectedDevices: [] as string[],
|
selectedDevices: [] as string[],
|
||||||
selectedLibraries: [] as string[],
|
selectedLibraries: [] as (string | number)[],
|
||||||
|
contentTypes: ["text", "image", "video"],
|
||||||
|
targetTags: [] as string[],
|
||||||
|
filterKeywords: [] as string[],
|
||||||
};
|
};
|
||||||
|
|
||||||
const NewMomentsSync: React.FC = () => {
|
const NewMomentsSync: React.FC = () => {
|
||||||
@@ -53,10 +58,15 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
startTime: res.timeRange?.start || "06:00",
|
startTime: res.timeRange?.start || "06:00",
|
||||||
endTime: res.timeRange?.end || "23:59",
|
endTime: res.timeRange?.end || "23:59",
|
||||||
syncCount: res.config?.syncCount || res.syncCount || 5,
|
syncCount: res.config?.syncCount || res.syncCount || 5,
|
||||||
|
syncInterval: res.config?.syncInterval || res.syncInterval || 30,
|
||||||
|
syncType: res.accountType === 1 ? 1 : 2,
|
||||||
accountType: res.accountType === 1 ? "business" : "personal",
|
accountType: res.accountType === 1 ? "business" : "personal",
|
||||||
enabled: res.status === 1,
|
enabled: res.status === 1,
|
||||||
selectedDevices: res.config?.devices || [],
|
selectedDevices: res.config?.devices || [],
|
||||||
selectedLibraries: res.config?.contentLibraryNames || [],
|
selectedLibraries: res.config?.contentLibraryNames || [],
|
||||||
|
contentTypes: res.config?.contentTypes || ["text", "image", "video"],
|
||||||
|
targetTags: res.config?.targetTags || [],
|
||||||
|
filterKeywords: res.config?.filterKeywords || [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -79,6 +89,15 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
setFormData((prev) => ({ ...prev, ...data }));
|
setFormData((prev) => ({ ...prev, ...data }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// UI选择账号类型时同步syncType和accountType
|
||||||
|
const handleAccountTypeChange = (type: "business" | "personal") => {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
accountType: type,
|
||||||
|
syncType: type === "business" ? 1 : 2,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
// 提交
|
// 提交
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!formData.taskName.trim()) {
|
if (!formData.taskName.trim()) {
|
||||||
@@ -98,13 +117,18 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
const params = {
|
const params = {
|
||||||
name: formData.taskName,
|
name: formData.taskName,
|
||||||
devices: formData.selectedDevices,
|
devices: formData.selectedDevices,
|
||||||
contentLibraries: formData.selectedLibraries,
|
contentLibraries: formData.selectedLibraries.map(Number),
|
||||||
|
syncInterval: formData.syncInterval,
|
||||||
syncCount: formData.syncCount,
|
syncCount: formData.syncCount,
|
||||||
|
syncType: formData.syncType, // 账号类型真实传参
|
||||||
|
accountType: formData.accountType === "business" ? 1 : 2, // 也要传
|
||||||
startTime: formData.startTime,
|
startTime: formData.startTime,
|
||||||
endTime: formData.endTime,
|
endTime: formData.endTime,
|
||||||
accountType: formData.accountType === "business" ? 1 : 2,
|
contentTypes: formData.contentTypes,
|
||||||
status: formData.enabled ? 1 : 2,
|
targetTags: formData.targetTags,
|
||||||
|
filterKeywords: formData.filterKeywords,
|
||||||
type: 2,
|
type: 2,
|
||||||
|
status: formData.enabled ? 1 : 2,
|
||||||
};
|
};
|
||||||
if (isEditMode && id) {
|
if (isEditMode && id) {
|
||||||
await updateMomentsSync({ id, ...params });
|
await updateMomentsSync({ id, ...params });
|
||||||
@@ -122,7 +146,7 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 步骤内容
|
// 步骤内容(去除按钮)
|
||||||
const renderStep = () => {
|
const renderStep = () => {
|
||||||
if (currentStep === 0) {
|
if (currentStep === 0) {
|
||||||
return (
|
return (
|
||||||
@@ -166,7 +190,7 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
updateForm({ syncCount: Math.max(1, formData.syncCount - 1) })
|
updateForm({ syncCount: Math.max(1, formData.syncCount - 1) })
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
-
|
<MinusOutlined />
|
||||||
</button>
|
</button>
|
||||||
<span className={style.counterValue}>{formData.syncCount}</span>
|
<span className={style.counterValue}>{formData.syncCount}</span>
|
||||||
<button
|
<button
|
||||||
@@ -175,7 +199,7 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
updateForm({ syncCount: formData.syncCount + 1 })
|
updateForm({ syncCount: formData.syncCount + 1 })
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
+
|
<PlusOutlined />
|
||||||
</button>
|
</button>
|
||||||
<span className={style.counterUnit}>条朋友圈</span>
|
<span className={style.counterUnit}>条朋友圈</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -186,13 +210,13 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
<div className={style.accountTypeRow}>
|
<div className={style.accountTypeRow}>
|
||||||
<button
|
<button
|
||||||
className={`${style.accountTypeBtn} ${formData.accountType === "business" ? style.accountTypeActive : ""}`}
|
className={`${style.accountTypeBtn} ${formData.accountType === "business" ? style.accountTypeActive : ""}`}
|
||||||
onClick={() => updateForm({ accountType: "business" })}
|
onClick={() => handleAccountTypeChange("business")}
|
||||||
>
|
>
|
||||||
业务号
|
业务号
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`${style.accountTypeBtn} ${formData.accountType === "personal" ? style.accountTypeActive : ""}`}
|
className={`${style.accountTypeBtn} ${formData.accountType === "personal" ? style.accountTypeActive : ""}`}
|
||||||
onClick={() => updateForm({ accountType: "personal" })}
|
onClick={() => handleAccountTypeChange("personal")}
|
||||||
>
|
>
|
||||||
人设号
|
人设号
|
||||||
</button>
|
</button>
|
||||||
@@ -209,12 +233,6 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style.formStepBtnRow}>
|
|
||||||
<Button type="primary" onClick={next} className={style.nextBtn}>
|
|
||||||
下一步
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -231,14 +249,6 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
selectedListMaxHeight={200}
|
selectedListMaxHeight={200}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.formStepBtnRow}>
|
|
||||||
<Button onClick={prev} className={style.prevBtn}>
|
|
||||||
上一步
|
|
||||||
</Button>
|
|
||||||
<Button type="primary" onClick={next} className={style.nextBtn}>
|
|
||||||
下一步
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -260,19 +270,57 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={style.formStepBtnRow}>
|
</div>
|
||||||
<Button onClick={prev} className={style.prevBtn}>
|
);
|
||||||
上一步
|
}
|
||||||
</Button>
|
return null;
|
||||||
<Button
|
};
|
||||||
type="primary"
|
|
||||||
onClick={handleSubmit}
|
// 统一底部按钮
|
||||||
loading={loading}
|
const renderFooter = () => {
|
||||||
className={style.completeBtn}
|
if (loading) return null;
|
||||||
>
|
if (currentStep === 0) {
|
||||||
完成
|
return (
|
||||||
</Button>
|
<div className={style.formStepBtnRow}>
|
||||||
</div>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
disabled={!formData.taskName.trim()}
|
||||||
|
onClick={next}
|
||||||
|
className={style.nextBtn}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (currentStep === 1) {
|
||||||
|
return (
|
||||||
|
<div className={style.formStepBtnRow}>
|
||||||
|
<Button onClick={prev} className={style.prevBtn} block>
|
||||||
|
上一步
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" onClick={next} className={style.nextBtn} block>
|
||||||
|
下一步
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (currentStep === 2) {
|
||||||
|
return (
|
||||||
|
<div className={style.formStepBtnRow}>
|
||||||
|
<Button onClick={prev} className={style.prevBtn} block>
|
||||||
|
上一步
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={loading}
|
||||||
|
className={style.completeBtn}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
完成
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -282,21 +330,9 @@ const NewMomentsSync: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
header={
|
header={
|
||||||
<NavBar
|
<NavCommon title={isEditMode ? "编辑朋友圈同步" : "新建朋友圈同步"} />
|
||||||
back={null}
|
|
||||||
style={{ background: "#fff" }}
|
|
||||||
left={
|
|
||||||
<div className="nav-title">
|
|
||||||
<ArrowLeftOutlined
|
|
||||||
twoToneColor="#1677ff"
|
|
||||||
onClick={() => navigate(-1)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isEditMode ? "编辑朋友圈同步" : "新建朋友圈同步"}
|
|
||||||
</NavBar>
|
|
||||||
}
|
}
|
||||||
|
footer={renderFooter()}
|
||||||
>
|
>
|
||||||
<div className={style.formBg}>
|
<div className={style.formBg}>
|
||||||
<StepIndicator currentStep={currentStep + 1} steps={steps} />
|
<StepIndicator currentStep={currentStep + 1} steps={steps} />
|
||||||
|
|||||||
Reference in New Issue
Block a user