From 527402425f1ef142607e8ba5d294a3150f4bbcc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Mon, 8 Sep 2025 16:41:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=9C=8B=E5=8F=8B=E5=9C=88=E5=90=8C?= =?UTF-8?q?=E6=AD=A5):=20=E6=96=B0=E5=A2=9E=E6=9C=8B=E5=8F=8B=E5=9C=88?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=AE=B0=E5=BD=95=E9=A1=B5=E9=9D=A2=E5=8F=8A?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构朋友圈同步模块结构,将原MomentsSync组件拆分为list和record两个子模块 添加朋友圈同步记录页面,包含搜索、分页和记录展示功能 优化样式文件组织,删除重复代码 新增api接口和数据模型定义 --- Cunkebao/dist/.vite/manifest.json | 18 +- Cunkebao/dist/index.html | 8 +- .../workspace/auto-like/record/index.tsx | 3 - .../auto-like/record/record.module.scss | 2 +- .../moments-sync/{ => list}/index.module.scss | 0 .../{MomentsSync.tsx => list/index.tsx} | 2 +- .../workspace/moments-sync/record/api.ts | 63 ++++ .../workspace/moments-sync/record/data.ts | 119 +++++++ .../workspace/moments-sync/record/index.tsx | 306 ++++++++++++++++++ .../moments-sync/record/record.module.scss | 268 +++++++++++++++ Cunkebao/src/router/module/workspace.tsx | 10 +- 11 files changed, 779 insertions(+), 20 deletions(-) rename Cunkebao/src/pages/mobile/workspace/moments-sync/{ => list}/index.module.scss (100%) rename Cunkebao/src/pages/mobile/workspace/moments-sync/{MomentsSync.tsx => list/index.tsx} (99%) create mode 100644 Cunkebao/src/pages/mobile/workspace/moments-sync/record/api.ts create mode 100644 Cunkebao/src/pages/mobile/workspace/moments-sync/record/data.ts create mode 100644 Cunkebao/src/pages/mobile/workspace/moments-sync/record/index.tsx create mode 100644 Cunkebao/src/pages/mobile/workspace/moments-sync/record/record.module.scss diff --git a/Cunkebao/dist/.vite/manifest.json b/Cunkebao/dist/.vite/manifest.json index e78197a2..435e8e43 100644 --- a/Cunkebao/dist/.vite/manifest.json +++ b/Cunkebao/dist/.vite/manifest.json @@ -1,14 +1,14 @@ { - "_charts-B9_ggjgM.js": { - "file": "assets/charts-B9_ggjgM.js", + "_charts-DrYFgxF3.js": { + "file": "assets/charts-DrYFgxF3.js", "name": "charts", "imports": [ - "_ui-CdpU1706.js", + "_ui-BCezweA9.js", "_vendor-2vc8h_ct.js" ] }, - "_ui-CdpU1706.js": { - "file": "assets/ui-CdpU1706.js", + "_ui-BCezweA9.js": { + "file": "assets/ui-BCezweA9.js", "name": "ui", "imports": [ "_vendor-2vc8h_ct.js" @@ -33,18 +33,18 @@ "name": "vendor" }, "index.html": { - "file": "assets/index-BZQSHOtN.js", + "file": "assets/index-B7uWDiaN.js", "name": "index", "src": "index.html", "isEntry": true, "imports": [ "_vendor-2vc8h_ct.js", "_utils-6WF66_dS.js", - "_ui-CdpU1706.js", - "_charts-B9_ggjgM.js" + "_ui-BCezweA9.js", + "_charts-DrYFgxF3.js" ], "css": [ - "assets/index-CciB7EKw.css" + "assets/index-DkU7m7k6.css" ] } } \ No newline at end of file diff --git a/Cunkebao/dist/index.html b/Cunkebao/dist/index.html index 6f790150..4ae4c1fd 100644 --- a/Cunkebao/dist/index.html +++ b/Cunkebao/dist/index.html @@ -11,13 +11,13 @@ - + - - + + - +
diff --git a/Cunkebao/src/pages/mobile/workspace/auto-like/record/index.tsx b/Cunkebao/src/pages/mobile/workspace/auto-like/record/index.tsx index fe08157e..1915fa48 100644 --- a/Cunkebao/src/pages/mobile/workspace/auto-like/record/index.tsx +++ b/Cunkebao/src/pages/mobile/workspace/auto-like/record/index.tsx @@ -137,9 +137,6 @@ export default function AutoLikeRecord() { onChange={handlePageChange} showSizeChanger={false} showQuickJumper - showTotal={(total, range) => - `第 ${range[0]}-${range[1]} 条,共 ${total} 条` - } size="default" className={styles.pagination} /> diff --git a/Cunkebao/src/pages/mobile/workspace/auto-like/record/record.module.scss b/Cunkebao/src/pages/mobile/workspace/auto-like/record/record.module.scss index a6df2c00..b63be1f0 100644 --- a/Cunkebao/src/pages/mobile/workspace/auto-like/record/record.module.scss +++ b/Cunkebao/src/pages/mobile/workspace/auto-like/record/record.module.scss @@ -3,7 +3,7 @@ display: flex; align-items: center; gap: 8px; - padding: 0 16px; + padding: 16px; } .headerSearchInputWrap { position: relative; diff --git a/Cunkebao/src/pages/mobile/workspace/moments-sync/index.module.scss b/Cunkebao/src/pages/mobile/workspace/moments-sync/list/index.module.scss similarity index 100% rename from Cunkebao/src/pages/mobile/workspace/moments-sync/index.module.scss rename to Cunkebao/src/pages/mobile/workspace/moments-sync/list/index.module.scss diff --git a/Cunkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx b/Cunkebao/src/pages/mobile/workspace/moments-sync/list/index.tsx similarity index 99% rename from Cunkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx rename to Cunkebao/src/pages/mobile/workspace/moments-sync/list/index.tsx index 3e81136d..1bd6d1d4 100644 --- a/Cunkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx +++ b/Cunkebao/src/pages/mobile/workspace/moments-sync/list/index.tsx @@ -124,7 +124,7 @@ const MomentsSync: React.FC = () => { } - onClick={() => navigate(`/workspace/moments-sync/${task.id}`)} + onClick={() => navigate(`/workspace/moments-sync/record/${task.id}`)} > 查看 diff --git a/Cunkebao/src/pages/mobile/workspace/moments-sync/record/api.ts b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/api.ts new file mode 100644 index 00000000..6142fde8 --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/api.ts @@ -0,0 +1,63 @@ +import request from "@/api/request"; +import { + LikeTask, + CreateLikeTaskData, + UpdateLikeTaskData, + LikeRecord, + PaginatedResponse, +} from "@/pages/workspace/auto-like/record/data"; + +// 获取自动点赞任务列表 +export function fetchAutoLikeTasks( + params = { type: 1, page: 1, limit: 100 }, +): Promise { + return request("/v1/workbench/list", params, "GET"); +} + +// 获取单个任务详情 +export function fetchAutoLikeTaskDetail(id: string): Promise { + return request("/v1/workbench/detail", { id }, "GET"); +} + +// 创建自动点赞任务 +export function createAutoLikeTask(data: CreateLikeTaskData): Promise { + return request("/v1/workbench/create", { ...data, type: 1 }, "POST"); +} + +// 更新自动点赞任务 +export function updateAutoLikeTask(data: UpdateLikeTaskData): Promise { + return request("/v1/workbench/update", { ...data, type: 1 }, "POST"); +} + +// 删除自动点赞任务 +export function deleteAutoLikeTask(id: string): Promise { + return request("/v1/workbench/delete", { id }, "DELETE"); +} + +// 切换任务状态 +export function toggleAutoLikeTask(id: string, status: string): Promise { + return request("/v1/workbench/update-status", { id, status }, "POST"); +} + +// 复制自动点赞任务 +export function copyAutoLikeTask(id: string): Promise { + return request("/v1/workbench/copy", { id }, "POST"); +} + +// 获取点赞记录 +export function fetchLikeRecords( + workbenchId: string, + page: number = 1, + limit: number = 20, + keyword?: string, +): Promise> { + const params: any = { + workbenchId, + page: page.toString(), + limit: limit.toString(), + }; + if (keyword) { + params.keyword = keyword; + } + return request("/v1/workbench/like-records", params, "GET"); +} diff --git a/Cunkebao/src/pages/mobile/workspace/moments-sync/record/data.ts b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/data.ts new file mode 100644 index 00000000..de39bd28 --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/data.ts @@ -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 { + code: number; + msg: string; + data: T; +} + +// 分页响应类型 +export interface PaginatedResponse { + list: T[]; + total: number; + page: number; + limit: number; +} diff --git a/Cunkebao/src/pages/mobile/workspace/moments-sync/record/index.tsx b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/index.tsx new file mode 100644 index 00000000..1915fa48 --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/index.tsx @@ -0,0 +1,306 @@ +import React, { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import { + Button, + Input, + Card, + Badge, + Avatar, + Skeleton, + message, + Spin, + Divider, + Pagination, +} from "antd"; +import { + LikeOutlined, + ReloadOutlined, + SearchOutlined, + UserOutlined, +} from "@ant-design/icons"; +import styles from "./record.module.scss"; +import NavCommon from "@/components/NavCommon"; +import { fetchLikeRecords } from "./api"; +import Layout from "@/components/Layout/Layout"; + +// 格式化日期 +const formatDate = (dateString: string) => { + try { + const date = new Date(dateString); + return date.toLocaleString("zh-CN", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + }); + } catch (error) { + return dateString; + } +}; + +export default function AutoLikeRecord() { + const { id } = useParams<{ id: string }>(); + const [records, setRecords] = useState([]); + const [recordsLoading, setRecordsLoading] = useState(false); + const [searchTerm, setSearchTerm] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [total, setTotal] = useState(0); + const pageSize = 10; + + useEffect(() => { + if (!id) return; + setRecordsLoading(true); + fetchLikeRecords(id, 1, pageSize) + .then((response: any) => { + setRecords(response.list || []); + setTotal(response.total || 0); + setCurrentPage(1); + }) + .catch(() => { + message.error("获取点赞记录失败,请稍后重试"); + }) + .finally(() => setRecordsLoading(false)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [id]); + + const handleSearch = () => { + setCurrentPage(1); + fetchLikeRecords(id!, 1, pageSize, searchTerm) + .then((response: any) => { + setRecords(response.list || []); + setTotal(response.total || 0); + setCurrentPage(1); + }) + .catch(() => { + message.error("获取点赞记录失败,请稍后重试"); + }); + }; + + const handleRefresh = () => { + fetchLikeRecords(id!, currentPage, pageSize, searchTerm) + .then((response: any) => { + setRecords(response.list || []); + setTotal(response.total || 0); + }) + .catch(() => { + message.error("获取点赞记录失败,请稍后重试"); + }); + }; + + const handlePageChange = (newPage: number) => { + fetchLikeRecords(id!, newPage, pageSize, searchTerm) + .then((response: any) => { + setRecords(response.list || []); + setTotal(response.total || 0); + setCurrentPage(newPage); + }) + .catch(() => { + message.error("获取点赞记录失败,请稍后重试"); + }); + }; + + return ( + + +
+
+ } + placeholder="搜索好友昵称或内容" + className={styles.headerSearchInput} + value={searchTerm} + onChange={e => setSearchTerm(e.target.value)} + onPressEnter={handleSearch} + allowClear + /> +
+
+ + } + footer={ + <> +
+ +
+ + } + > +
+
+ {recordsLoading ? ( +
+ {Array.from({ length: 3 }).map((_, index) => ( +
+
+ +
+ + +
+
+ +
+ + +
+ + +
+
+
+ ))} +
+ ) : records.length === 0 ? ( +
+ +

暂无点赞记录

+
+ ) : ( + <> + {records.map(record => ( +
+
+
+ } + size={40} + className={styles.avatarImg} + /> +
+
+ {record.friendName} +
+
内容发布者
+
+
+ +
+ +
+ {record.content && ( +

{record.content}

+ )} + {Array.isArray(record.resUrls) && + record.resUrls.length > 0 && ( +
+ {record.resUrls + .slice(0, 9) + .map((image: string, idx: number) => ( +
+ {`内容图片 +
+ ))} +
+ )} +
+
+ } + size={32} + className={styles.operatorAvatar} + /> +
+ + {record.operatorName} + + + + 已赞 + +
+
+
+ ))} + + )} +
+
+
+ ); +} diff --git a/Cunkebao/src/pages/mobile/workspace/moments-sync/record/record.module.scss b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/record.module.scss new file mode 100644 index 00000000..b63be1f0 --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/moments-sync/record/record.module.scss @@ -0,0 +1,268 @@ +// 搜索栏 +.headerSearchBar { + display: flex; + align-items: center; + gap: 8px; + padding: 16px; +} +.headerSearchInputWrap { + position: relative; + flex: 1; +} +.headerSearchIcon { + position: absolute; + left: 12px; + top: 10px; + width: 16px; + height: 16px; + color: #a3a3a3; +} +.headerSearchInput { + padding-left: 32px !important; +} +.spin { + animation: spin 1s linear infinite; +} +@keyframes spin { + 100% { + transform: rotate(360deg); + } +} + +// 分页 +.footerPagination { + display: flex; + justify-content: center; + align-items: center; + padding: 12px 0; + background: #fff; +} +.pagination { + :global(.ant-pagination-item) { + border-radius: 6px; + } + :global(.ant-pagination-item-active) { + background: #1890ff; + border-color: #1890ff; + } + :global(.ant-pagination-prev), + :global(.ant-pagination-next) { + border-radius: 6px; + } + :global(.ant-pagination-jump-prev), + :global(.ant-pagination-jump-next) { + border-radius: 6px; + } +} + +// 背景和内容 +.bgWrap { + background: #f7f7fa; + min-height: 100vh; + padding-bottom: 80px; +} +.contentWrap { + padding: 0 16px; + display: flex; + flex-direction: column; + gap: 16px; +} + +// 骨架屏 +.skeletonWrap { + display: flex; + flex-direction: column; + gap: 16px; +} +.skeletonCard { + padding: 0px; +} +.skeletonCardHeader { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 12px; +} +.skeletonAvatar { + width: 40px; + height: 40px; + border-radius: 50%; +} +.skeletonNameWrap { + display: flex; + flex-direction: column; + gap: 8px; +} +.skeletonName { + width: 96px; + height: 16px; +} +.skeletonSub { + width: 64px; + height: 12px; +} +.skeletonSep { + margin: 12px 0; +} +.skeletonContentWrap { + display: flex; + flex-direction: column; + gap: 8px; +} +.skeletonContent1 { + width: 100%; + height: 16px; +} +.skeletonContent2 { + width: 75%; + height: 16px; +} +.skeletonImgWrap { + display: flex; + gap: 8px; + margin-top: 12px; +} +.skeletonImg { + width: 80px; + height: 80px; + border-radius: 8px; +} + +// 空状态 +.emptyWrap { + text-align: center; + padding: 48px 0; +} +.emptyIcon { + width: 48px; + height: 48px; + color: #e5e7eb; + margin: 0 auto 12px auto; +} +.emptyText { + color: #888; + font-size: 16px; +} + +// 记录卡片 +.recordCard { + background: #fff; + border-radius: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + padding: 16px; +} +.recordCardHeader { + display: flex; + align-items: flex-start; + justify-content: space-between; +} +.recordCardHeaderLeft { + display: flex; + align-items: center; + gap: 12px; + max-width: 65%; +} +.avatarImg { + width: 40px; + height: 40px; + border-radius: 50%; +} +.friendInfo { + min-width: 0; +} +.friendName { + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.friendSub { + font-size: 13px; + color: #888; +} +.timeBadge { + background: #e8f0fe; + white-space: nowrap; + flex-shrink: 0; +} +.cardSep { + margin: 12px 0; +} +.cardContent { + margin-bottom: 12px; +} +.contentText { + color: #444; + margin-bottom: 12px; + white-space: pre-line; +} +.imgGrid { + display: grid; + gap: 8px; +} +.grid1 { + grid-template-columns: 1fr; +} +.grid2 { + grid-template-columns: 1fr 1fr; +} +.grid3 { + grid-template-columns: 1fr 1fr 1fr; +} +.grid6 { + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr; +} +.grid9 { + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr 1fr; +} +.imgItem { + position: relative; + aspect-ratio: 1/1; + border-radius: 8px; + overflow: hidden; +} +.img { + width: 100%; + height: 100%; + object-fit: cover; +} + +// 操作人 +.operatorWrap { + display: flex; + align-items: center; + margin-top: 16px; + padding: 8px; + background: #f3f4f6; + border-radius: 8px; +} +.operatorAvatar { + width: 32px !important; + height: 32px !important; + margin-right: 8px; + flex-shrink: 0; +} +.operatorInfo { + font-size: 14px; + position: relative; + flex: 1; + position: relative; +} +.operatorName { + font-weight: 500; + max-width: 100%; + display: inline-block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.operatorAction { + color: #888; + margin-left: 8px; + font-size: 12px; + position: absolute; + right: 0; + top: 2px; +} diff --git a/Cunkebao/src/router/module/workspace.tsx b/Cunkebao/src/router/module/workspace.tsx index 5c010122..c4b365e6 100644 --- a/Cunkebao/src/router/module/workspace.tsx +++ b/Cunkebao/src/router/module/workspace.tsx @@ -8,9 +8,10 @@ import AutoGroupForm from "@/pages/mobile/workspace/auto-group/form"; import GroupPush from "@/pages/mobile/workspace/group-push/list"; import FormGroupPush from "@/pages/mobile/workspace/group-push/form"; import DetailGroupPush from "@/pages/mobile/workspace/group-push/detail"; -import MomentsSync from "@/pages/mobile/workspace/moments-sync/MomentsSync"; -import MomentsSyncDetail from "@/pages/mobile/workspace/moments-sync/Detail"; +import MomentsSync from "@/pages/mobile/workspace/moments-sync/list"; import NewMomentsSync from "@/pages/mobile/workspace/moments-sync/new/index"; +import MomentsSyncDetail from "@/pages/mobile/workspace/moments-sync/Detail"; +import MomentsSyncRecord from "@/pages/mobile/workspace/moments-sync/record"; import AIAssistant from "@/pages/mobile/workspace/ai-assistant/AIAssistant"; import TrafficDistribution from "@/pages/mobile/workspace/traffic-distribution/list/index"; import TrafficDistributionDetail from "@/pages/mobile/workspace/traffic-distribution/detail/index"; @@ -103,6 +104,11 @@ const workspaceRoutes = [ element: , auth: true, }, + { + path: "/workspace/moments-sync/record/:id", + element: , + auth: true, + }, { path: "/workspace/moments-sync/edit/:id", element: ,