This commit is contained in:
超级老白兔
2025-08-05 18:06:57 +08:00
parent ed99243890
commit a1ae5e9364
5 changed files with 168 additions and 209 deletions

View File

@@ -1,26 +1,23 @@
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { Button, Card, ProgressBar, Popover, Toast, NavBar } from "antd-mobile";
import { Button, Card, Popover, Toast } from "antd-mobile";
import { Input, Switch } from "antd";
import {
MoreOutline,
AddCircleOutline,
UserAddOutline,
ClockCircleOutline,
TeamOutline,
CalendarOutline,
} from "antd-mobile-icons";
import {
ReloadOutlined,
SettingOutlined,
PlusOutlined,
ArrowLeftOutlined,
SearchOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
import style from "./index.module.scss";
import NavCommon from "@/components/NavCommon";
interface GroupTask {
id: string;
@@ -167,25 +164,14 @@ const AutoGroupList: React.FC = () => {
<Layout
header={
<>
<NavBar
back={null}
style={{ background: "#fff" }}
left={
<div className="nav-title">
<ArrowLeftOutlined
twoToneColor="#1677ff"
onClick={() => navigate(-1)}
/>
</div>
}
<NavCommon
title="自动建群"
right={
<Button size="small" color="primary" onClick={handleCreateNew}>
<PlusOutlined />
</Button>
}
>
<span className="nav-title"></span>
</NavBar>
/>
{/* 搜索栏 */}
<div className="search-bar">
<div className="search-input-wrapper">

View File

@@ -0,0 +1,119 @@
// 自动点赞任务状态
export type LikeTaskStatus = 1 | 2; // 1: 开启, 2: 关闭
// 内容类型
export type ContentType = "text" | "image" | "video" | "link";
// 设备信息
export interface Device {
id: string;
name: string;
status: "online" | "offline";
lastActive: string;
}
// 好友信息
export interface Friend {
id: string;
nickname: string;
wechatId: string;
avatar: string;
tags: string[];
region: string;
source: string;
}
// 点赞记录
export interface LikeRecord {
id: string;
workbenchId: string;
momentsId: string;
snsId: string;
wechatAccountId: string;
wechatFriendId: string;
likeTime: string;
content: string;
resUrls: string[];
momentTime: string;
userName: string;
operatorName: string;
operatorAvatar: string;
friendName: string;
friendAvatar: string;
}
// 自动点赞任务
export interface LikeTask {
id: string;
name: string;
status: LikeTaskStatus;
deviceCount: number;
targetGroup: string;
likeCount: number;
lastLikeTime: string;
createTime: string;
creator: string;
likeInterval: number;
maxLikesPerDay: number;
timeRange: { start: string; end: string };
contentTypes: ContentType[];
targetTags: string[];
devices: string[];
friends: string[];
friendMaxLikes: number;
friendTags: string;
enableFriendTags: boolean;
todayLikeCount: number;
totalLikeCount: number;
updateTime: string;
}
// 创建任务数据
export interface CreateLikeTaskData {
name: string;
interval: number;
maxLikes: number;
startTime: string;
endTime: string;
contentTypes: ContentType[];
devices: string[];
friends?: string[];
friendMaxLikes: number;
friendTags?: string;
enableFriendTags: boolean;
targetTags: string[];
}
// 更新任务数据
export interface UpdateLikeTaskData extends CreateLikeTaskData {
id: string;
}
// 任务配置
export interface TaskConfig {
interval: number;
maxLikes: number;
startTime: string;
endTime: string;
contentTypes: ContentType[];
devices: string[];
friends: string[];
friendMaxLikes: number;
friendTags: string;
enableFriendTags: boolean;
}
// API响应类型
export interface ApiResponse<T = any> {
code: number;
msg: string;
data: T;
}
// 分页响应类型
export interface PaginatedResponse<T> {
list: T[];
total: number;
page: number;
limit: number;
}

View File

@@ -14,7 +14,6 @@ import {
MoreOutlined,
LikeOutlined,
} from "@ant-design/icons";
import { ArrowLeftOutlined } from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
import {
@@ -23,7 +22,7 @@ import {
toggleAutoLikeTask,
copyAutoLikeTask,
} from "./api";
import { LikeTask } from "@/pages/workspace/auto-like/record/data";
import { LikeTask } from "./data";
import style from "./index.module.scss";
// 卡片菜单组件

View File

@@ -1,37 +1,13 @@
import request from "@/api/request";
export interface GroupPushTask {
id: string;
name: string;
status: number; // 1: 运行中, 2: 已暂停
deviceCount: number;
targetGroups: string[];
pushCount: number;
successCount: number;
lastPushTime: string;
createTime: string;
creator: string;
pushInterval: number;
maxPushPerDay: number;
timeRange: { start: string; end: string };
messageType: "text" | "image" | "video" | "link";
messageContent: string;
targetTags: string[];
pushMode: "immediate" | "scheduled";
scheduledTime?: string;
}
interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
export async function fetchGroupPushTasks(): Promise<GroupPushTask[]> {
const response = await request("/v1/workbench/list", { type: 3 }, "GET");
if (Array.isArray(response)) return response;
if (response && Array.isArray(response.data)) return response.data;
return [];
export async function fetchGroupPushTasks() {
return request("/v1/workbench/list", { type: 3 }, "GET");
}
export async function deleteGroupPushTask(id: string): Promise<ApiResponse> {

View File

@@ -36,7 +36,6 @@ import {
deleteGroupPushTask,
toggleGroupPushTask,
copyGroupPushTask,
GroupPushTask,
} from "./index.api";
import styles from "./index.module.scss";
@@ -44,14 +43,14 @@ const GroupPush: React.FC = () => {
const navigate = useNavigate();
const [expandedTaskId, setExpandedTaskId] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const [tasks, setTasks] = useState<GroupPushTask[]>([]);
const [tasks, setTasks] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const fetchTasks = async () => {
setLoading(true);
try {
const list = await fetchGroupPushTasks();
setTasks(list);
const result = await fetchGroupPushTasks();
setTasks(result.list);
} finally {
setLoading(false);
}
@@ -180,13 +179,7 @@ const GroupPush: React.FC = () => {
allowClear
size="large"
/>
<Button
className={styles["refresh-btn"]}
size="small"
onClick={fetchTasks}
loading={loading}
>
<Button size="small" onClick={fetchTasks} loading={loading}>
<ReloadOutlined />
</Button>
</div>
@@ -228,39 +221,35 @@ const GroupPush: React.FC = () => {
onChange={() => toggleTaskStatus(task.id)}
/>
<Dropdown
overlay={
<Menu>
<Menu.Item
key="view"
icon={<EyeOutlined />}
onClick={() => handleView(task.id)}
>
</Menu.Item>
<Menu.Item
key="edit"
icon={<EditOutlined />}
onClick={() => handleEdit(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>
}
menu={{
items: [
{
key: "view",
icon: <EyeOutlined />,
label: "查看",
onClick: () => handleView(task.id),
},
{
key: "edit",
icon: <EditOutlined />,
label: "编辑",
onClick: () => handleEdit(task.id),
},
{
key: "copy",
icon: <CopyOutlined />,
label: "复制",
onClick: () => handleCopy(task.id),
},
{
key: "delete",
icon: <DeleteOutlined />,
label: "删除",
danger: true,
onClick: () => handleDelete(task.id),
},
],
}}
trigger={["click"]}
>
<Button icon={<MoreOutlined />} />
@@ -268,131 +257,21 @@ const GroupPush: React.FC = () => {
</div>
</div>
<div className={styles.taskInfoGrid}>
<div>{task.deviceCount} </div>
<div>{task.targetGroups.length} </div>
<div>{task.deviceCount || 1} </div>
<div>{task.config?.groups?.length || 0} </div>
<div>
{task.successCount}/{task.pushCount}
{task.successCount || 0}/{task.pushCount || 0}
</div>
<div>{task.creator}</div>
</div>
<div className={styles.progressBlock}>
<div className={styles.progressLabel}></div>
<Progress
percent={getSuccessRate(task.pushCount, task.successCount)}
size="small"
/>
<div>{task.creatorName || task.creator}</div>
</div>
<div className={styles.taskFooter}>
<div>
<ClockCircleOutlined /> {task.lastPushTime}
</div>
<div>
{task.createTime}
<Button
type="link"
size="small"
icon={
expandedTaskId === task.id ? (
<UpOutlined />
) : (
<DownOutlined />
)
}
onClick={() => toggleExpand(task.id)}
/>
<ClockCircleOutlined />
{task.config?.lastPushTime || "暂无"}
</div>
<div>{task.createTime}</div>
</div>
{expandedTaskId === task.id && (
<div className={styles.expandedPanel}>
<div className={styles.expandedGrid}>
<div>
<SettingOutlined /> <b></b>
<div>{task.pushInterval} </div>
<div>{task.maxPushPerDay} </div>
<div>
{task.timeRange.start} -{" "}
{task.timeRange.end}
</div>
<div>
{task.pushMode === "immediate"
? "立即推送"
: "定时推送"}
</div>
{task.scheduledTime && (
<div>{task.scheduledTime}</div>
)}
</div>
<div>
<TeamOutlined /> <b></b>
<div
style={{ display: "flex", flexWrap: "wrap", gap: 4 }}
>
{task.targetGroups.map(group => (
<Badge
key={group}
color="blue"
text={group}
style={{ background: "#f0f5ff", marginRight: 4 }}
/>
))}
</div>
</div>
<div>
<MessageOutlined /> <b></b>
<div>
{getMessageTypeText(task.messageType)}
</div>
<div
style={{
background: "#f5f5f5",
padding: 8,
borderRadius: 4,
marginTop: 4,
}}
>
{task.messageContent}
</div>
</div>
<div>
<CalendarOutlined /> <b></b>
<div>
{task.pushCount} / {task.maxPushPerDay}
</div>
<Progress
percent={Math.round(
(task.pushCount / task.maxPushPerDay) * 100,
)}
size="small"
/>
{task.targetTags.length > 0 && (
<div style={{ marginTop: 8 }}>
<div></div>
<div
style={{
display: "flex",
flexWrap: "wrap",
gap: 4,
}}
>
{task.targetTags.map(tag => (
<Badge
key={tag}
color="purple"
text={tag}
style={{
background: "#f9f0ff",
marginRight: 4,
}}
/>
))}
</div>
</div>
)}
</div>
</div>
</div>
)}
</Card>
))
)}