feat: 本次提交更新内容如下
朋友圈同步列表完成
This commit is contained in:
@@ -1,8 +1,186 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import PlaceholderPage from "@/components/PlaceholderPage";
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
|
import { Button, Switch, message, Spin, Badge } from "antd";
|
||||||
|
import {
|
||||||
|
ArrowLeftOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
DatabaseOutlined,
|
||||||
|
MobileOutlined,
|
||||||
|
} 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;
|
||||||
|
updateTime?: string;
|
||||||
|
maxSyncPerDay?: number;
|
||||||
|
syncInterval?: number;
|
||||||
|
timeRange?: { start: string; end: string };
|
||||||
|
contentTypes?: string[];
|
||||||
|
targetTags?: string[];
|
||||||
|
todaySyncCount?: number;
|
||||||
|
totalSyncCount?: number;
|
||||||
|
syncMode?: string;
|
||||||
|
config?: {
|
||||||
|
devices?: string[];
|
||||||
|
contentLibraryNames?: string[];
|
||||||
|
syncCount?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusText = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 1:
|
||||||
|
return "进行中";
|
||||||
|
case 2:
|
||||||
|
return "已暂停";
|
||||||
|
default:
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const MomentsSyncDetail: React.FC = () => {
|
const MomentsSyncDetail: React.FC = () => {
|
||||||
return <PlaceholderPage title="朋友圈同步详情" />;
|
const { id } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [task, setTask] = useState<MomentsSyncTask | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const fetchTaskDetail = useCallback(async () => {
|
||||||
|
if (!id) return;
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await request("/v1/workbench/detail", { id }, "GET");
|
||||||
|
if (res) setTask(res);
|
||||||
|
} catch {
|
||||||
|
message.error("获取任务详情失败");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) fetchTaskDetail();
|
||||||
|
}, [id, fetchTaskDetail]);
|
||||||
|
|
||||||
|
const handleToggleStatus = async () => {
|
||||||
|
if (!task || !id) return;
|
||||||
|
try {
|
||||||
|
const newStatus = task.status === 1 ? 2 : 1;
|
||||||
|
await request(
|
||||||
|
"/v1/workbench/update-status",
|
||||||
|
{ id, status: newStatus },
|
||||||
|
"POST"
|
||||||
|
);
|
||||||
|
setTask({ ...task, status: newStatus });
|
||||||
|
message.success(newStatus === 1 ? "任务已开启" : "任务已暂停");
|
||||||
|
} catch {
|
||||||
|
message.error("操作失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = () => {
|
||||||
|
if (id) navigate(`/workspace/moments-sync/edit/${id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className={style.detailLoading}>
|
||||||
|
<Spin size="large" />
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!task) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className={style.detailLoading}>
|
||||||
|
<div>任务不存在</div>
|
||||||
|
<Button onClick={() => navigate("/workspace/moments-sync")}>
|
||||||
|
返回列表
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<div className={style.headerBar}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<ArrowLeftOutlined />}
|
||||||
|
onClick={() => navigate("/workspace/moments-sync")}
|
||||||
|
className={style.backBtn}
|
||||||
|
/>
|
||||||
|
<span className={style.title}>任务详情</span>
|
||||||
|
<Button
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
onClick={handleEdit}
|
||||||
|
className={style.editBtn}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={style.detailBg}>
|
||||||
|
<div className={style.detailCard}>
|
||||||
|
<div className={style.detailTop}>
|
||||||
|
<div className={style.detailTitle}>{task.name}</div>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
task.status === 1
|
||||||
|
? style.statusPill + " " + style.statusActive
|
||||||
|
: style.statusPill + " " + style.statusPaused
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{getStatusText(task.status)}
|
||||||
|
</span>
|
||||||
|
<Switch
|
||||||
|
checked={task.status === 1}
|
||||||
|
onChange={handleToggleStatus}
|
||||||
|
className={style.switchBtn}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={style.detailInfoRow}>
|
||||||
|
<div className={style.infoCol}>
|
||||||
|
推送设备:{task.config?.devices?.length || 0} 个
|
||||||
|
</div>
|
||||||
|
<div className={style.infoCol}>
|
||||||
|
内容库:{task.config?.contentLibraryNames?.join(",") || "-"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={style.detailInfoRow}>
|
||||||
|
<div className={style.infoCol}>
|
||||||
|
已同步:{task.syncCount || 0} 条
|
||||||
|
</div>
|
||||||
|
<div className={style.infoCol}>创建人:{task.creatorName}</div>
|
||||||
|
</div>
|
||||||
|
<div className={style.detailBottom}>
|
||||||
|
<div className={style.bottomLeft}>
|
||||||
|
<ClockCircleOutlined className={style.clockIcon} />
|
||||||
|
上次同步:{task.lastSyncTime || "无"}
|
||||||
|
</div>
|
||||||
|
<div className={style.bottomRight}>创建时间:{task.createTime}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 可继续补充更多详情卡片,如同步设置、同步记录等 */}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MomentsSyncDetail;
|
export default MomentsSyncDetail;
|
||||||
|
|||||||
@@ -1,13 +1,273 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import PlaceholderPage from "@/components/PlaceholderPage";
|
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 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 (
|
return (
|
||||||
<PlaceholderPage
|
<Layout
|
||||||
title="朋友圈同步"
|
header={
|
||||||
showAddButton
|
<>
|
||||||
addButtonText="新建同步"
|
<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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
387
nkebao/src/pages/workspace/moments-sync/index.module.scss
Normal file
387
nkebao/src/pages/workspace/moments-sync/index.module.scss
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
.pageBg {
|
||||||
|
background: #f8f6f3;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerBar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
padding: 0 16px;
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #188eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backBtn {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
color: #666;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addBtn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
background: #f8f6f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInput {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskList {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskCard {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 16px rgba(24,142,238,0.10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskCardTop {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskName {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #222;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchBtn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionBtn {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskCardInfo {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyBox {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||||
|
padding: 40px 0 32px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #bbb;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyText {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #888;
|
||||||
|
margin: 16px 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemCard {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px 16px 12px 16px;
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 16px rgba(24,142,238,0.10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemTop {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemTitle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemName {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #222;
|
||||||
|
margin-right: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusBadge {
|
||||||
|
margin-left: 2px;
|
||||||
|
.ant-badge-status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
.ant-badge-status-success {
|
||||||
|
background: #19c37d;
|
||||||
|
}
|
||||||
|
.ant-badge-status-default {
|
||||||
|
background: #bdbdbd;
|
||||||
|
}
|
||||||
|
.ant-badge-status-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemActions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchBtn {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreBtn {
|
||||||
|
margin-left: 2px;
|
||||||
|
color: #888;
|
||||||
|
font-size: 18px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemInfoRow {
|
||||||
|
display: flex;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.infoCol {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemBottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
border-top: 1px solid #f3f3f3;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
.bottomLeft {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.clockIcon {
|
||||||
|
color: #bdbdbd;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
.bottomRight {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 覆盖Antd Dropdown菜单样式
|
||||||
|
.ant-dropdown-menu {
|
||||||
|
border-radius: 10px !important;
|
||||||
|
box-shadow: 0 4px 16px rgba(0,0,0,0.12) !important;
|
||||||
|
min-width: 110px !important;
|
||||||
|
padding: 6px 0 !important;
|
||||||
|
}
|
||||||
|
.ant-dropdown-menu-item {
|
||||||
|
font-size: 14px !important;
|
||||||
|
padding: 7px 16px !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
.ant-dropdown-menu-item:hover {
|
||||||
|
background: #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
.ant-dropdown-menu-item-danger {
|
||||||
|
color: #e53e3e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailBg {
|
||||||
|
background: #f8f6f3;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 24px 0 32px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailCard {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||||
|
padding: 20px 20px 12px 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 480px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailTop {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailTitle {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusBadge {
|
||||||
|
margin-left: 2px;
|
||||||
|
.ant-badge-status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
.ant-badge-status-success {
|
||||||
|
background: #19c37d;
|
||||||
|
}
|
||||||
|
.ant-badge-status-default {
|
||||||
|
background: #bdbdbd;
|
||||||
|
}
|
||||||
|
.ant-badge-status-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchBtn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailInfoRow {
|
||||||
|
display: flex;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.infoCol {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailBottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
border-top: 1px solid #f3f3f3;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
.bottomLeft {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.clockIcon {
|
||||||
|
color: #bdbdbd;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
.bottomRight {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailLoading {
|
||||||
|
min-height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #888;
|
||||||
|
font-size: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusPill {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 48px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-size: 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
|
margin-left: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.statusActive {
|
||||||
|
background: #19c37d;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.statusPaused {
|
||||||
|
background: #e5e7eb;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user