275 lines
8.1 KiB
TypeScript
275 lines
8.1 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
||
import { useNavigate } from "react-router-dom";
|
||
import { Button, Switch, Input, message, Badge, Dropdown, Menu } from "antd";
|
||
import {
|
||
PlusOutlined,
|
||
SearchOutlined,
|
||
ReloadOutlined,
|
||
EyeOutlined,
|
||
EditOutlined,
|
||
DeleteOutlined,
|
||
CopyOutlined,
|
||
MoreOutlined,
|
||
ClockCircleOutlined,
|
||
ArrowLeftOutlined,
|
||
} from "@ant-design/icons";
|
||
import Layout from "@/components/Layout/Layout";
|
||
import style from "./index.module.scss";
|
||
import request from "@/api/request";
|
||
|
||
interface MomentsSyncTask {
|
||
id: string;
|
||
name: string;
|
||
status: 1 | 2;
|
||
deviceCount: number;
|
||
syncCount: number;
|
||
lastSyncTime: string;
|
||
createTime: string;
|
||
creatorName: string;
|
||
contentLib?: string;
|
||
config?: { devices?: string[]; contentLibraryNames?: string[] };
|
||
}
|
||
|
||
const getStatusText = (status: number) => {
|
||
switch (status) {
|
||
case 1:
|
||
return "进行中";
|
||
case 2:
|
||
return "已暂停";
|
||
default:
|
||
return "未知";
|
||
}
|
||
};
|
||
|
||
const MomentsSync: React.FC = () => {
|
||
const navigate = useNavigate();
|
||
const [searchTerm, setSearchTerm] = useState("");
|
||
const [loading, setLoading] = useState(false);
|
||
const [tasks, setTasks] = useState<MomentsSyncTask[]>([]);
|
||
|
||
const fetchTasks = async () => {
|
||
setLoading(true);
|
||
try {
|
||
const res = await request(
|
||
"/v1/workbench/list",
|
||
{ type: 2, page: 1, limit: 100 },
|
||
"GET"
|
||
);
|
||
setTasks(res.list || []);
|
||
} catch (e) {
|
||
message.error("获取任务失败");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchTasks();
|
||
}, []);
|
||
|
||
const handleDelete = async (id: string) => {
|
||
if (!window.confirm("确定要删除该任务吗?")) return;
|
||
try {
|
||
await request("/v1/workbench/delete", { id }, "DELETE");
|
||
message.success("删除成功");
|
||
fetchTasks();
|
||
} catch {
|
||
message.error("删除失败");
|
||
}
|
||
};
|
||
|
||
const handleCopy = async (id: string) => {
|
||
try {
|
||
await request("/v1/workbench/copy", { id }, "POST");
|
||
message.success("复制成功");
|
||
fetchTasks();
|
||
} catch {
|
||
message.error("复制失败");
|
||
}
|
||
};
|
||
|
||
const handleToggle = async (id: string, status: number) => {
|
||
const newStatus = status === 1 ? 2 : 1;
|
||
try {
|
||
await request(
|
||
"/v1/workbench/update-status",
|
||
{ id, status: newStatus },
|
||
"POST"
|
||
);
|
||
setTasks((prev) =>
|
||
prev.map((t) => (t.id === id ? { ...t, status: newStatus } : t))
|
||
);
|
||
message.success("操作成功");
|
||
} catch {
|
||
message.error("操作失败");
|
||
}
|
||
};
|
||
|
||
const filteredTasks = tasks.filter((task) =>
|
||
task.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||
);
|
||
|
||
// 菜单
|
||
const getMenu = (task: MomentsSyncTask) => (
|
||
<Menu>
|
||
<Menu.Item
|
||
key="view"
|
||
icon={<EyeOutlined />}
|
||
onClick={() => navigate(`/workspace/moments-sync/${task.id}`)}
|
||
>
|
||
查看
|
||
</Menu.Item>
|
||
<Menu.Item
|
||
key="edit"
|
||
icon={<EditOutlined />}
|
||
onClick={() => navigate(`/workspace/moments-sync/edit/${task.id}`)}
|
||
>
|
||
编辑
|
||
</Menu.Item>
|
||
<Menu.Item
|
||
key="copy"
|
||
icon={<CopyOutlined />}
|
||
onClick={() => handleCopy(task.id)}
|
||
>
|
||
复制
|
||
</Menu.Item>
|
||
<Menu.Item
|
||
key="delete"
|
||
icon={<DeleteOutlined />}
|
||
onClick={() => handleDelete(task.id)}
|
||
danger
|
||
>
|
||
删除
|
||
</Menu.Item>
|
||
</Menu>
|
||
);
|
||
|
||
return (
|
||
<Layout
|
||
header={
|
||
<>
|
||
<div className={style.headerBar}>
|
||
<Button
|
||
type="text"
|
||
icon={<ArrowLeftOutlined />}
|
||
onClick={() => navigate("/workspace")}
|
||
className={style.backBtn}
|
||
/>
|
||
<span className={style.title}>朋友圈同步</span>
|
||
<Button
|
||
type="primary"
|
||
icon={<PlusOutlined />}
|
||
onClick={() => navigate("/workspace/moments-sync/new")}
|
||
className={style.addBtn}
|
||
>
|
||
新建任务
|
||
</Button>
|
||
</div>
|
||
<div className={style.searchBar}>
|
||
<Input
|
||
placeholder="搜索任务名称"
|
||
prefix={<SearchOutlined />}
|
||
value={searchTerm}
|
||
onChange={(e) => setSearchTerm(e.target.value)}
|
||
onPressEnter={fetchTasks}
|
||
className={style.searchInput}
|
||
/>
|
||
<Button
|
||
icon={<ReloadOutlined />}
|
||
onClick={fetchTasks}
|
||
loading={loading}
|
||
/>
|
||
</div>
|
||
</>
|
||
}
|
||
>
|
||
<div className={style.pageBg}>
|
||
<div className={style.taskList}>
|
||
{filteredTasks.length === 0 ? (
|
||
<div className={style.emptyBox}>
|
||
<span style={{ fontSize: 40, color: "#ddd" }}>
|
||
<ClockCircleOutlined />
|
||
</span>
|
||
<div className={style.emptyText}>暂无同步任务</div>
|
||
<Button
|
||
type="primary"
|
||
onClick={() => navigate("/workspace/moments-sync/new")}
|
||
>
|
||
新建第一个任务
|
||
</Button>
|
||
</div>
|
||
) : (
|
||
filteredTasks.map((task) => (
|
||
<div key={task.id} className={style.itemCard}>
|
||
<div className={style.itemTop}>
|
||
<div className={style.itemTitle}>
|
||
<span className={style.itemName}>{task.name}</span>
|
||
<span
|
||
className={
|
||
task.status === 1
|
||
? style.statusPill + " " + style.statusActive
|
||
: style.statusPill + " " + style.statusPaused
|
||
}
|
||
>
|
||
{getStatusText(task.status)}
|
||
</span>
|
||
</div>
|
||
<div className={style.itemActions}>
|
||
<Switch
|
||
checked={task.status === 1}
|
||
onChange={() => handleToggle(task.id, task.status)}
|
||
className={style.switchBtn}
|
||
size="small"
|
||
/>
|
||
<Dropdown
|
||
overlay={getMenu(task)}
|
||
trigger={["click"]}
|
||
placement="bottomRight"
|
||
>
|
||
<Button
|
||
type="text"
|
||
icon={<MoreOutlined />}
|
||
className={style.moreBtn}
|
||
/>
|
||
</Dropdown>
|
||
</div>
|
||
</div>
|
||
<div className={style.itemInfoRow}>
|
||
<div className={style.infoCol}>
|
||
推送设备:{task.config?.devices?.length || 0} 个
|
||
</div>
|
||
<div className={style.infoCol}>
|
||
已同步:{task.syncCount || 0} 条
|
||
</div>
|
||
</div>
|
||
<div className={style.itemInfoRow}>
|
||
<div className={style.infoCol}>
|
||
内容库:
|
||
{task.config?.contentLibraryNames?.join(",") ||
|
||
task.contentLib ||
|
||
"默认内容库"}
|
||
</div>
|
||
<div className={style.infoCol}>
|
||
创建人:{task.creatorName}
|
||
</div>
|
||
</div>
|
||
<div className={style.itemBottom}>
|
||
<div className={style.bottomLeft}>
|
||
<ClockCircleOutlined className={style.clockIcon} />
|
||
上次同步:{task.lastSyncTime || "无"}
|
||
</div>
|
||
<div className={style.bottomRight}>
|
||
创建时间:{task.createTime}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))
|
||
)}
|
||
</div>
|
||
</div>
|
||
</Layout>
|
||
);
|
||
};
|
||
|
||
export default MomentsSync;
|