From 55fe2b46df831b9ddf6db167834d286905e2abcd Mon Sep 17 00:00:00 2001
From: wong <106998207@qq.com>
Date: Wed, 10 Dec 2025 17:56:55 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../mobile/mine/wechat-accounts/detail/api.ts | 42 ++-
.../wechat-accounts/detail/detail.module.scss | 183 +++++------
.../mine/wechat-accounts/detail/index.tsx | 289 +++++++++++++-----
.../controller/wechat/PostTransferFriends.php | 2 +-
4 files changed, 351 insertions(+), 165 deletions(-)
diff --git a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts
index 10dfb60a..8c14f3e9 100644
--- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts
+++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts
@@ -94,6 +94,20 @@ export async function exportWechatMoments(params: {
}
);
+ // 检查响应类型,如果是JSON错误响应,需要解析错误信息
+ const contentType = response.headers["content-type"] || "";
+ if (contentType.includes("application/json")) {
+ // 如果是JSON响应,说明可能是错误信息
+ const text = await response.data.text();
+ const errorData = JSON.parse(text);
+ throw new Error(errorData.message || errorData.msg || "导出失败");
+ }
+
+ // 检查响应状态
+ if (response.status !== 200) {
+ throw new Error(`导出失败,状态码: ${response.status}`);
+ }
+
// 创建下载链接
const blob = new Blob([response.data]);
const url = window.URL.createObjectURL(blob);
@@ -116,6 +130,32 @@ export async function exportWechatMoments(params: {
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error: any) {
- throw new Error(error.response?.data?.message || error.message || "导出失败");
+ // 如果是我们抛出的错误,直接抛出
+ if (error.message && error.message !== "导出失败") {
+ throw error;
+ }
+
+ // 处理axios错误响应
+ if (error.response) {
+ // 如果响应是blob类型,尝试读取为文本
+ if (error.response.data instanceof Blob) {
+ try {
+ const text = await error.response.data.text();
+ const errorData = JSON.parse(text);
+ throw new Error(errorData.message || errorData.msg || "导出失败");
+ } catch (parseError) {
+ throw new Error("导出失败,请重试");
+ }
+ } else {
+ throw new Error(
+ error.response.data?.message ||
+ error.response.data?.msg ||
+ error.message ||
+ "导出失败"
+ );
+ }
+ } else {
+ throw new Error(error.message || "导出失败");
+ }
}
}
diff --git a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/detail.module.scss b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/detail.module.scss
index b06f237d..95c33d8a 100644
--- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/detail.module.scss
+++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/detail.module.scss
@@ -373,6 +373,12 @@
font-weight: 600;
color: #52c41a;
}
+
+ .stat-value-negative {
+ font-size: 20px;
+ font-weight: 600;
+ color: #ff4d4f;
+ }
}
}
@@ -868,9 +874,10 @@
.type-selector {
display: flex;
gap: 8px;
- flex-wrap: wrap;
+ width: 100%;
.type-option {
+ flex: 1;
padding: 8px 16px;
border: 1px solid #e0e0e0;
border-radius: 8px;
@@ -879,6 +886,7 @@
cursor: pointer;
transition: all 0.2s;
background: white;
+ text-align: center;
&:hover {
border-color: #1677ff;
@@ -1331,82 +1339,54 @@
}
}
+.moments-action-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 16px;
+ background: white;
+ border-bottom: 1px solid #f0f0f0;
+
+ .action-button, .action-button-dark {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ border-radius: 8px;
+ background: #1677ff;
+ border: none;
+ cursor: pointer;
+ transition: all 0.2s;
+ color: white;
+
+ &:active {
+ background: #0958d9;
+ transform: scale(0.95);
+ }
+
+ svg {
+ font-size: 20px;
+ color: white;
+ }
+ }
+
+ .action-button-dark {
+ background: #1677ff;
+ color: white;
+
+ &:active {
+ background: #0958d9;
+ }
+ }
+}
+
.moments-content {
padding: 16px 0;
height: 500px;
overflow-y: auto;
background: #f5f5f5;
- .moments-action-bar {
- display: flex;
- justify-content: space-between;
- padding: 0 16px 16px;
-
- .action-button, .action-button-dark {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 70px;
- height: 40px;
- border-radius: 8px;
- background: #1677ff;
-
- .action-icon-text, .action-icon-image, .action-icon-video, .action-icon-export {
- width: 20px;
- height: 20px;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 4px;
- margin-bottom: 2px;
- position: relative;
-
- &::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 12px;
- height: 2px;
- background: white;
- }
- }
-
- .action-icon-image::after {
- content: '';
- position: absolute;
- top: 6px;
- left: 6px;
- width: 8px;
- height: 8px;
- border-radius: 2px;
- background: white;
- }
-
- .action-icon-video::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 0;
- height: 0;
- border-style: solid;
- border-width: 5px 0 5px 8px;
- border-color: transparent transparent transparent white;
- }
-
- .action-text, .action-text-light {
- font-size: 12px;
- color: white;
- }
- }
-
- .action-button-dark {
- background: #333;
- }
- }
-
.moments-list {
padding: 0 16px;
@@ -1459,7 +1439,7 @@
.image-grid {
display: grid;
- gap: 8px;
+ gap: 4px;
width: 100%;
// 1张图片:宽度拉伸,高度自适应
@@ -1469,56 +1449,57 @@
img {
width: 100%;
height: auto;
+ max-height: 400px;
object-fit: cover;
- border-radius: 8px;
+ border-radius: 4px;
}
}
- // 2张图片:左右并列
+ // 2张图片:左右并列,1:1比例
&.double {
grid-template-columns: 1fr 1fr;
img {
width: 100%;
- height: 120px;
+ aspect-ratio: 1 / 1;
object-fit: cover;
- border-radius: 8px;
+ border-radius: 4px;
}
}
- // 3张图片:三张并列
+ // 3张图片:三张并列,1:1比例
&.triple {
grid-template-columns: 1fr 1fr 1fr;
img {
width: 100%;
- height: 100px;
+ aspect-ratio: 1 / 1;
object-fit: cover;
- border-radius: 8px;
+ border-radius: 4px;
}
}
- // 4张图片:2x2网格布局
+ // 4张图片:2x2网格布局,1:1比例
&.quad {
grid-template-columns: repeat(2, 1fr);
img {
width: 100%;
- height: 140px;
+ aspect-ratio: 1 / 1;
object-fit: cover;
- border-radius: 8px;
+ border-radius: 4px;
}
}
- // 5张及以上:网格布局(9宫格)
+ // 5张及以上:网格布局(9宫格),1:1比例
&.grid {
grid-template-columns: repeat(3, 1fr);
img {
width: 100%;
- height: 100px;
+ aspect-ratio: 1 / 1;
object-fit: cover;
- border-radius: 8px;
+ border-radius: 4px;
}
.image-more {
@@ -1526,11 +1507,11 @@
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
- border-radius: 8px;
+ border-radius: 4px;
color: white;
font-size: 12px;
font-weight: 500;
- height: 100px;
+ aspect-ratio: 1 / 1;
}
}
}
@@ -1547,6 +1528,34 @@
}
}
}
+
+ .moments-loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 16px 0;
+ }
+
+ .moments-no-more {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 16px 0;
+ }
+ }
+
+ .friends-loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 16px 0;
+ }
+
+ .friends-no-more {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 16px 0;
}
}
diff --git a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx
index 1f6e9e28..ae694b3a 100644
--- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx
+++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx
@@ -12,13 +12,18 @@ import {
Tag,
Switch,
DatePicker,
+ InfiniteScroll,
} from "antd-mobile";
-import { Input, Pagination } from "antd";
+import { Input } from "antd";
import NavCommon from "@/components/NavCommon";
import {
SearchOutlined,
ReloadOutlined,
UserOutlined,
+ FileTextOutlined,
+ PictureOutlined,
+ VideoCameraOutlined,
+ DownloadOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
import style from "./detail.module.scss";
@@ -32,6 +37,7 @@ import {
} from "./api";
import DeviceSelection from "@/components/DeviceSelection";
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
+import dayjs from "dayjs";
import { WechatAccountSummary, Friend, MomentItem } from "./data";
@@ -107,6 +113,84 @@ const WechatAccountDetail: React.FC = () => {
}
}, [id]);
+ // 计算账号价值
+ // 规则:
+ // 1. 1个好友3块
+ // 2. 1个群1块
+ // 3. 修改过微信号10块
+ const calculateAccountValue = useCallback(() => {
+ // 获取好友数量(优先使用概览数据,其次使用好友列表总数,最后使用账号信息)
+ const friendsCount = overviewData?.totalFriends || friendsTotal || accountInfo?.friendShip?.totalFriend || 0;
+
+ // 获取群数量(优先使用概览数据,其次使用账号信息)
+ const groupsCount = overviewData?.highValueChatrooms || accountInfo?.friendShip?.groupNumber || 0;
+
+ // 判断是否修改过微信号
+ // 注意:需要根据实际API返回的字段来判断,可能的字段名:
+ // - isWechatIdModified (布尔值)
+ // - wechatIdModified (布尔值)
+ // - hasModifiedWechatId (布尔值)
+ // - wechatIdChangeCount (数字,大于0表示修改过)
+ // 如果API没有返回该字段,需要后端添加或根据其他逻辑判断
+ const isWechatIdModified =
+ accountInfo?.isWechatIdModified ||
+ accountInfo?.wechatIdModified ||
+ accountInfo?.hasModifiedWechatId ||
+ (accountInfo?.wechatIdChangeCount && accountInfo.wechatIdChangeCount > 0) ||
+ false;
+
+ // 计算各部分价值
+ const friendsValue = friendsCount * 3; // 好友数 * 3
+ const groupsValue = groupsCount * 1; // 群数 * 1
+ const wechatIdModifiedValue = isWechatIdModified ? 10 : 0; // 修改过微信号 ? 10 : 0
+
+ // 计算总价值
+ const totalValue = friendsValue + groupsValue + wechatIdModifiedValue;
+
+ return {
+ value: totalValue,
+ formatted: `¥${totalValue.toLocaleString()}`,
+ breakdown: {
+ friends: friendsValue,
+ groups: groupsValue,
+ wechatIdModified: wechatIdModifiedValue,
+ friendsCount,
+ groupsCount,
+ isWechatIdModified,
+ },
+ };
+ }, [overviewData, friendsTotal, accountInfo]);
+
+ // 计算今日价值变化
+ // 规则:
+ // 1. 今日新增好友 * 3块
+ // 2. 今日新增群 * 1块
+ const calculateTodayValueChange = useCallback(() => {
+ // 获取今日新增好友数
+ const todayNewFriends = overviewData?.todayNewFriends || accountSummary?.statistics?.todayAdded || 0;
+
+ // 获取今日新增群数
+ const todayNewChatrooms = overviewData?.todayNewChatrooms || 0;
+
+ // 计算今日价值变化
+ const friendsValueChange = todayNewFriends * 3; // 今日新增好友数 * 3
+ const groupsValueChange = todayNewChatrooms * 1; // 今日新增群数 * 1
+
+ const totalChange = friendsValueChange + groupsValueChange;
+
+ return {
+ change: totalChange,
+ formatted: totalChange >= 0 ? `+${totalChange.toLocaleString()}` : `${totalChange.toLocaleString()}`,
+ isPositive: totalChange >= 0,
+ breakdown: {
+ friends: friendsValueChange,
+ groups: groupsValueChange,
+ todayNewFriends,
+ todayNewChatrooms,
+ },
+ };
+ }, [overviewData, accountSummary]);
+
// 获取概览数据
const fetchOverviewData = useCallback(async () => {
if (!id) return;
@@ -122,7 +206,7 @@ const WechatAccountDetail: React.FC = () => {
// 获取好友列表 - 封装为独立函数
const fetchFriendsList = useCallback(
- async (page: number = 1, keyword: string = "") => {
+ async (page: number = 1, keyword: string = "", append: boolean = false) => {
if (!id) return;
setIsFetchingFriends(true);
@@ -132,7 +216,7 @@ const WechatAccountDetail: React.FC = () => {
const response = await getWechatFriends({
wechatAccount: id,
page: page,
- limit: 5,
+ limit: 20,
keyword: keyword,
});
@@ -175,15 +259,17 @@ const WechatAccountDetail: React.FC = () => {
};
});
- setFriends(newFriends);
+ setFriends(prev => (append ? [...prev, ...newFriends] : newFriends));
setFriendsTotal(response.total);
setFriendsPage(page);
- setIsFriendsEmpty(newFriends.length === 0);
+ setIsFriendsEmpty(newFriends.length === 0 && !append);
} catch (error) {
console.error("获取好友列表失败:", error);
setHasFriendLoadError(true);
- setFriends([]);
- setIsFriendsEmpty(true);
+ if (!append) {
+ setFriends([]);
+ setIsFriendsEmpty(true);
+ }
Toast.show({
content: "获取好友列表失败,请检查网络连接",
position: "top",
@@ -238,22 +324,20 @@ const WechatAccountDetail: React.FC = () => {
// 搜索好友
const handleSearch = useCallback(() => {
setFriendsPage(1);
- fetchFriendsList(1, searchQuery);
+ fetchFriendsList(1, searchQuery, false);
}, [searchQuery, fetchFriendsList]);
// 刷新好友列表
const handleRefreshFriends = useCallback(() => {
- fetchFriendsList(friendsPage, searchQuery);
+ fetchFriendsList(friendsPage, searchQuery, false);
}, [friendsPage, searchQuery, fetchFriendsList]);
- // 分页切换
- const handlePageChange = useCallback(
- (page: number) => {
- setFriendsPage(page);
- fetchFriendsList(page, searchQuery);
- },
- [searchQuery, fetchFriendsList],
- );
+ // 加载更多好友
+ const handleLoadMoreFriends = async () => {
+ if (isFetchingFriends) return;
+ if (friends.length >= friendsTotal) return;
+ await fetchFriendsList(friendsPage + 1, searchQuery, true);
+ };
// 初始化数据
useEffect(() => {
@@ -268,7 +352,7 @@ const WechatAccountDetail: React.FC = () => {
if (activeTab === "friends" && id) {
setIsFriendsEmpty(false);
setHasFriendLoadError(false);
- fetchFriendsList(1, searchQuery);
+ fetchFriendsList(1, searchQuery, false);
}
}, [activeTab, id, fetchFriendsList, searchQuery]);
@@ -298,8 +382,8 @@ const WechatAccountDetail: React.FC = () => {
setInheritInfo(true);
// 设置默认打招呼内容,使用当前微信账号昵称
const nickname = accountInfo?.nickname || "未知";
- setGreeting(`这个是${nickname}的新号,之前那个号没用了,重新加一下您`);
- setFirstMessage("");
+ setGreeting(`我是${nickname}的新号,请通过`);
+ setFirstMessage("这个是我的新号,重新加你一下,以后业务就用这个号!");
setShowTransferConfirm(true);
};
@@ -385,10 +469,10 @@ const WechatAccountDetail: React.FC = () => {
navigate(`/mine/traffic-pool/detail/${friend.wechatId}/${friend.id}`);
};
- const handleLoadMoreMoments = () => {
+ const handleLoadMoreMoments = async () => {
if (isFetchingMoments) return;
if (moments.length >= momentsTotal) return;
- fetchMomentsList(momentsPage + 1, true);
+ await fetchMomentsList(momentsPage + 1, true);
};
// 处理朋友圈导出
@@ -398,6 +482,18 @@ const WechatAccountDetail: React.FC = () => {
return;
}
+ // 验证时间范围不超过1个月
+ if (exportStartTime && exportEndTime) {
+ const maxDate = dayjs(exportStartTime).add(1, "month").toDate();
+ if (exportEndTime > maxDate) {
+ Toast.show({
+ content: "日期范围不能超过1个月",
+ position: "top",
+ });
+ return;
+ }
+ }
+
setExportLoading(true);
try {
// 格式化时间
@@ -418,18 +514,27 @@ const WechatAccountDetail: React.FC = () => {
});
Toast.show({ content: "导出成功", position: "top" });
- setShowExportPopup(false);
- // 重置筛选条件
+ // 重置筛选条件(先重置,再关闭弹窗)
setExportKeyword("");
setExportType(undefined);
setExportStartTime(null);
setExportEndTime(null);
+ setShowStartTimePicker(false);
+ setShowEndTimePicker(false);
+ // 延迟关闭弹窗,确保Toast显示
+ setTimeout(() => {
+ setShowExportPopup(false);
+ }, 500);
} catch (error: any) {
console.error("导出失败:", error);
+ const errorMessage = error?.message || "导出失败,请重试";
Toast.show({
- content: error.message || "导出失败,请重试",
+ content: errorMessage,
position: "top",
+ duration: 2000,
});
+ // 确保loading状态被重置
+ setExportLoading(false);
} finally {
setExportLoading(false);
}
@@ -548,7 +653,7 @@ const WechatAccountDetail: React.FC = () => {
- {overviewData?.accountValue?.formatted || `¥${overviewData?.accountValue?.value || "29,800"}`}
+ {calculateAccountValue().formatted}
@@ -558,8 +663,8 @@ const WechatAccountDetail: React.FC = () => {
今日价值变化
-
- {overviewData?.todayValueChange?.formatted || `+${overviewData?.todayValueChange?.change || "500"}`}
+
+ {calculateTodayValueChange().formatted}
@@ -761,7 +866,7 @@ const WechatAccountDetail: React.FC = () => {
好友总估值
- {overviewData?.accountValue?.formatted || "¥1,500,000"}
+ {calculateAccountValue().formatted}
@@ -780,7 +885,7 @@ const WechatAccountDetail: React.FC = () => {