From fb362a56af0a24453762f3dafc533352ecb57b18 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: Sat, 13 Sep 2025 15:08:42 +0800 Subject: [PATCH 01/18] =?UTF-8?q?refactor(mobile):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E4=BA=A4=E4=BA=92=E5=92=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除未使用的导入和状态变量 将点击事件移至容器元素提升交互体验 减少好友列表每页显示数量为5条 --- .../src/pages/mobile/mine/devices/index.tsx | 11 ++--- .../mobile/mine/traffic-pool/detail/index.tsx | 3 +- .../mine/wechat-accounts/detail/index.tsx | 47 ++++--------------- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/Cunkebao/src/pages/mobile/mine/devices/index.tsx b/Cunkebao/src/pages/mobile/mine/devices/index.tsx index 62fe17f2..f84b30c7 100644 --- a/Cunkebao/src/pages/mobile/mine/devices/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/devices/index.tsx @@ -291,7 +291,10 @@ const Devices: React.FC = () => { {/* 主要内容区域:头像和详细信息 */} -
+
goDetail(device.id!)} + > {/* 头像 */}
{device.avatar ? ( @@ -339,12 +342,6 @@ const Devices: React.FC = () => {
- - {/* 箭头图标 */} - goDetail(device.id!)} - /> ))} diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index 4c7ce51b..66265d8a 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { Card, Button, Avatar, Tag, List, SpinLoading } from "antd-mobile"; import { UserOutlined, @@ -24,7 +24,6 @@ import styles from "./index.module.scss"; const TrafficPoolDetail: React.FC = () => { const { wxid, userId } = useParams(); - const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [user, setUser] = useState(null); const [activeTab, setActiveTab] = useState("basic"); 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 25271b23..d1e5a6ee 100644 --- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx @@ -17,10 +17,6 @@ import { SearchOutlined, ReloadOutlined, UserOutlined, - ClockCircleOutlined, - MessageOutlined, - StarOutlined, - ExclamationCircleOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; import style from "./detail.module.scss"; @@ -39,7 +35,6 @@ const WechatAccountDetail: React.FC = () => { const [showTransferConfirm, setShowTransferConfirm] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [activeTab, setActiveTab] = useState("overview"); - const [isLoading, setIsLoading] = useState(false); const [loadingInfo, setLoadingInfo] = useState(true); // 好友列表相关状态 @@ -91,7 +86,7 @@ const WechatAccountDetail: React.FC = () => { const response = await getWechatFriends({ wechatAccount: id, page: page, - limit: 20, + limit: 5, keyword: keyword, }); @@ -185,35 +180,6 @@ const WechatAccountDetail: React.FC = () => { return colors[Math.floor(Math.random() * colors.length)]; }; - const calculateAccountAge = (registerTime: string) => { - const registerDate = new Date(registerTime); - const now = new Date(); - const diffTime = Math.abs(now.getTime() - registerDate.getTime()); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - const years = Math.floor(diffDays / 365); - const months = Math.floor((diffDays % 365) / 30); - return { years, months }; - }; - - const formatAccountAge = (age: { years: number; months: number }) => { - if (age.years > 0) { - return `${age.years}年${age.months}个月`; - } - return `${age.months}个月`; - }; - - const getWeightColor = (weight: number) => { - if (weight >= 80) return "text-green-600"; - if (weight >= 60) return "text-yellow-600"; - return "text-red-600"; - }; - - const getWeightDescription = (weight: number) => { - if (weight >= 80) return "账号质量优秀,可以正常使用"; - if (weight >= 60) return "账号质量良好,需要注意使用频率"; - return "账号质量较差,建议谨慎使用"; - }; - const handleTransferFriends = () => { setShowTransferConfirm(true); }; @@ -257,6 +223,10 @@ const WechatAccountDetail: React.FC = () => { setActiveTab(value); }; + const handleFriendClick = (friend: Friend) => { + navigate(`/mine/traffic-pool/detail/${friend.wechatId}/${friend.id}`); + }; + return ( } loading={loadingInfo}>
@@ -415,7 +385,11 @@ const WechatAccountDetail: React.FC = () => { ) : ( <> {friends.map(friend => ( -
+
handleFriendClick(friend)} + > { total={Math.ceil(friendsTotal / 20)} current={friendsPage} onChange={handlePageChange} - showText={true} />
)} From 268afb7209ed8367b87fba3eba6d46ba339a79ac 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: Sat, 13 Sep 2025 15:47:57 +0800 Subject: [PATCH 02/18] =?UTF-8?q?feat(traffic-pool):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=B5=81=E9=87=8F=E6=B1=A0=E8=AF=A6=E6=83=85=E9=A1=B5=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=BB=93=E6=9E=84=E5=92=8C=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新API接口路径和数据结构以匹配新后端实现 - 重构用户详情类型定义和数据处理逻辑 - 优化页面样式和布局 - 实现基于RMM评分的动态标签显示 - 完善统计数据的展示逻辑 --- .../mobile/mine/traffic-pool/detail/api.ts | 2 +- .../mobile/mine/traffic-pool/detail/data.ts | 87 +++++--- .../traffic-pool/detail/index.module.scss | 8 +- .../mobile/mine/traffic-pool/detail/index.tsx | 195 +++++++++++++----- .../mobile/mine/traffic-pool/list/index.tsx | 2 +- 5 files changed, 203 insertions(+), 91 deletions(-) diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index 6d4c4b83..3f3341ae 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -2,7 +2,7 @@ import request from "@/api/request"; import type { UserTagsResponse } from "./data"; export function getTrafficPoolDetail(wechatId: string) { - return request("/v1/wechats/getWechatInfo", { wechatId }, "GET"); + return request("/v1/traffic/pool/getUserInfo", { wechatId }, "GET"); } // 获取用户旅程记录 diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts index 43615992..f32ec073 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts @@ -1,39 +1,64 @@ +// 设备信息类型 +export interface DeviceInfo { + id: number; + memo: string; + imei: string; + brand: string; + alive: number; + address: string; +} + +// 来源信息类型 +export interface SourceInfo { + nickname: string; + avatar: string; + gender: number; + phone: string; + wechatId: string; + alias: string; + createTime: string; + friendId: number; + wechatAccountId: number; + lastMsgTime: string; + device: DeviceInfo; +} + +// 统计总计类型 +export interface TotalStats { + msg: number; + money: number; + isFriend: boolean; + percentage: string; +} + +// RMM评分类型 +export interface RmmScore { + r: number; + f: number; + m: number; +} + // 用户详情类型 export interface TrafficPoolUserDetail { id: number; + identifier: string; + wechatId: string; nickname: string; avatar: string; - wechatId: string; - status: number | string; - addTime: string; - lastInteraction: string; - deviceName?: string; - wechatAccountName?: string; - customerServiceName?: string; - poolNames?: string[]; - rfmScore?: { - recency: number; - frequency: number; - monetary: number; - segment?: string; - }; - totalSpent?: number; - interactionCount?: number; - conversionRate?: number; - tags?: string[]; - packages?: string[]; - interactions?: Array<{ - id: string; - type: string; - content: string; - timestamp: string; - value?: number; - }>; + gender: number; + phone: string; + alias: string; + lastMsgTime: string; + source: SourceInfo[]; + packages: any[]; + total: TotalStats; + rmm: RmmScore; } // 扩展的用户详情类型 export interface ExtendedUserDetail extends TrafficPoolUserDetail { - userInfo: { + // 保留原有的扩展字段用于向后兼容 + userInfo?: { nickname: string; avatar: string; wechatId: string; @@ -44,23 +69,23 @@ export interface ExtendedUserDetail extends TrafficPoolUserDetail { unknowFriend: number; }; }; - rfmScore: { + rfmScore?: { recency: number; frequency: number; monetary: number; totalScore: number; }; - trafficPools: { + trafficPools?: { currentPool: string; availablePools: string[]; }; - userTags: Array<{ + userTags?: Array<{ id: string; name: string; color: string; type: string; }>; - valueTags: Array<{ + valueTags?: Array<{ id: string; name: string; color: string; diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss index 6b896216..23fd44d2 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss @@ -1,9 +1,3 @@ -.container { - padding: 0; - background: #f5f5f5; - min-height: 100vh; -} - // 头部样式 .header { display: flex; @@ -123,7 +117,7 @@ // 内容区域 .content { - padding: 16px; + padding: 10px 10px 10px 16px; } .tabContent { diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index 66265d8a..786669ed 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -22,6 +22,21 @@ import type { } from "./data"; import styles from "./index.module.scss"; +// RMM评分辅助函数 +const getRmmValueLevel = (totalScore: number): string => { + if (totalScore >= 12) return "高价值客户"; + if (totalScore >= 8) return "中等价值客户"; + if (totalScore >= 4) return "低价值客户"; + return "潜在客户"; +}; + +const getRmmColor = (totalScore: number): string => { + if (totalScore >= 12) return "danger"; + if (totalScore >= 8) return "warning"; + if (totalScore >= 4) return "primary"; + return "default"; +}; + const TrafficPoolDetail: React.FC = () => { const { wxid, userId } = useParams(); const [loading, setLoading] = useState(true); @@ -45,43 +60,89 @@ const TrafficPoolDetail: React.FC = () => { setLoading(true); getTrafficPoolDetail(wxid as string) .then(res => { - // 将API数据转换为扩展的用户详情数据 + // 直接使用API返回的数据结构 const extendedUser: ExtendedUserDetail = { ...res, - // 添加userInfo属性 - userInfo: res.userInfo, - // 模拟RFM评分数据 + // 根据新数据结构构建userInfo + userInfo: { + nickname: res.nickname, + avatar: res.avatar, + wechatId: res.wechatId, + friendShip: { + totalFriend: res.source?.length || 0, + maleFriend: res.source?.filter(s => s.gender === 1).length || 0, + femaleFriend: res.source?.filter(s => s.gender === 2).length || 0, + unknowFriend: res.source?.filter(s => s.gender === 0).length || 0, + }, + }, + // 使用API返回的RMM数据 rfmScore: { - recency: 5, - frequency: 5, - monetary: 5, - totalScore: 15, + recency: res.rmm.r, + frequency: res.rmm.f, + monetary: res.rmm.m, + totalScore: res.rmm.r + res.rmm.f + res.rmm.m, }, - // 模拟流量池数据 + // 根据数据推断流量池信息 trafficPools: { - currentPool: "新用户池", - availablePools: ["高价值客户池", "活跃用户池"], + currentPool: res.total.isFriend ? "已添加好友池" : "待添加池", + availablePools: ["高价值客户池", "活跃用户池", "新用户池"], }, - // 模拟用户标签数据 + // 基于数据生成用户标签 userTags: [ - { id: "1", name: "近期活跃", color: "success", type: "user" }, - { id: "2", name: "高频互动", color: "primary", type: "user" }, - { id: "3", name: "高消费", color: "warning", type: "user" }, - { id: "4", name: "老客户", color: "danger", type: "user" }, + ...(res.total.isFriend + ? [ + { + id: "friend", + name: "已添加好友", + color: "success", + type: "status", + }, + ] + : []), + ...(res.total.money > 0 + ? [ + { + id: "paid", + name: "付费用户", + color: "warning", + type: "value", + }, + ] + : []), + ...(res.total.msg > 10 + ? [ + { + id: "active", + name: "高频互动", + color: "primary", + type: "behavior", + }, + ] + : []), + ...(res.source?.length > 1 + ? [ + { + id: "multi", + name: "多设备用户", + color: "danger", + type: "device", + }, + ] + : []), ], - // 模拟价值标签数据 + // 基于RMM评分生成价值标签 valueTags: [ { - id: "1", - name: "重要保持客户", - color: "primary", + id: "rmm", + name: getRmmValueLevel(res.rmm.r + res.rmm.f + res.rmm.m), + color: getRmmColor(res.rmm.r + res.rmm.f + res.rmm.m), icon: "crown", - rfmScore: 14, - valueLevel: "高价值", + rfmScore: res.rmm.r + res.rmm.f + res.rmm.m, + valueLevel: getRmmValueLevel(res.rmm.r + res.rmm.f + res.rmm.m), }, ], }; - console.log(extendedUser); + console.log("用户详情数据:", extendedUser); setUser(extendedUser); }) @@ -257,7 +318,7 @@ const TrafficPoolDetail: React.FC = () => {
@@ -266,20 +327,29 @@ const TrafficPoolDetail: React.FC = () => { } />
-
{user.userInfo.nickname}
-
{user.userInfo.wechatId}
+
{user.nickname}
+
{user.wechatId}
- - - 重要价值客户 - - - 优先添加 - + {user.valueTags?.map(tag => ( + + + {tag.name} + + ))} + {user.total.isFriend && ( + + 已添加好友 + + )}
@@ -322,11 +392,29 @@ const TrafficPoolDetail: React.FC = () => { {/* 关联信息 */} - 设备 - 微信号 - 客服 - 添加时间 - 最近互动 + + 设备 + + 微信号 + 别名 + + 添加时间 + + + 最近互动 + @@ -404,7 +492,7 @@ const TrafficPoolDetail: React.FC = () => { className={styles.statValue} style={{ color: "#52c41a" }} > - ¥9561 + ¥{user.total.money || 0}
总消费
@@ -413,7 +501,7 @@ const TrafficPoolDetail: React.FC = () => { className={styles.statValue} style={{ color: "#1677ff" }} > - 6 + {user.total.msg || 0}
互动次数
@@ -422,13 +510,18 @@ const TrafficPoolDetail: React.FC = () => { className={styles.statValue} style={{ color: "#722ed1" }} > - 3% + {user.total.percentage || "0"}%
转化率
-
- 未添加 +
+ {user.total.isFriend ? "已添加" : "未添加"}
添加状态
@@ -443,7 +536,7 @@ const TrafficPoolDetail: React.FC = () => { className={styles.statValue} style={{ color: "#1677ff" }} > - {user.userInfo.friendShip.totalFriend} + {user.userInfo?.friendShip.totalFriend || 0}
总好友
@@ -452,7 +545,7 @@ const TrafficPoolDetail: React.FC = () => { className={styles.statValue} style={{ color: "#1677ff" }} > - {user.userInfo.friendShip.maleFriend} + {user.userInfo?.friendShip.maleFriend || 0}
男性好友
@@ -461,13 +554,13 @@ const TrafficPoolDetail: React.FC = () => { className={styles.statValue} style={{ color: "#eb2f96" }} > - {user.userInfo.friendShip.femaleFriend} + {user.userInfo?.friendShip.femaleFriend || 0}
女性好友
- {user.userInfo.friendShip.unknowFriend} + {user.userInfo?.friendShip.unknowFriend || 0}
未知性别
diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx b/Cunkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx index 0c08b5e6..84fefee2 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx @@ -209,7 +209,7 @@ const TrafficPoolList: React.FC = () => { style={{ cursor: "pointer" }} onClick={() => navigate( - `/mine/traffic-pool/detail/${item.sourceId}/${item.id}`, + `/mine/traffic-pool/detail/${item.wechatId}/${item.id}`, ) } > From 9f01ad06656c1d988e29ad5b1561420d06e50a59 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: Sat, 13 Sep 2025 16:03:21 +0800 Subject: [PATCH 03/18] =?UTF-8?q?feat(=E5=BE=AE=E4=BF=A1=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E8=AF=A6=E6=83=85):=20=E6=B7=BB=E5=8A=A0=E9=A3=8E=E9=99=A9?= =?UTF-8?q?=E8=AF=84=E4=BC=B0=E6=A0=87=E7=AD=BE=E9=A1=B5=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E9=99=90=E5=88=B6=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增风险评估标签页,用于展示微信账号的限制记录,包括限制原因、日期和风险等级。当没有限制记录时显示"暂无风险记录"。 --- .../wechat-accounts/detail/detail.module.scss | 71 +++++++++++++++++++ .../mine/wechat-accounts/detail/index.tsx | 40 +++++++++++ 2 files changed, 111 insertions(+) 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 944ab4ff..c77c7dd4 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 @@ -738,3 +738,74 @@ min-width: 70px; margin-right: 8px; } + +.risk-content { + padding: 16px 0; + height: 500px; + overflow-y: auto; + + .restrictions-list { + .restriction-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + margin-bottom: 12px; + background: #fff; + border-radius: 8px; + border: 1px solid #f0f0f0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); + + &:last-child { + margin-bottom: 0; + } + + .restriction-info { + flex: 1; + + .restriction-reason { + font-size: 14px; + color: #333; + font-weight: 500; + margin-bottom: 4px; + } + + .restriction-date { + font-size: 12px; + color: #999; + } + } + + .restriction-level { + .level-badge { + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + + &.level-1 { + background: #e8f5e8; + color: #52c41a; + } + + &.level-2 { + background: #fff7e6; + color: #fa8c16; + } + + &.level-3 { + background: #fff2f0; + color: #ff4d4f; + } + } + } + } + } + + .empty { + text-align: center; + color: #999; + font-size: 14px; + padding: 40px 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 d1e5a6ee..5ca717f8 100644 --- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx @@ -439,6 +439,46 @@ const WechatAccountDetail: React.FC = () => { )} + + +
+ {accountSummary?.restrictions && + accountSummary.restrictions.length > 0 ? ( +
+ {accountSummary.restrictions.map(restriction => ( +
+
+
+ {restriction.reason} +
+
+ {restriction.date + ? formatDateTime(restriction.date) + : "暂无时间"} +
+
+
+ + {restriction.level === 1 + ? "低风险" + : restriction.level === 2 + ? "中风险" + : "高风险"} + +
+
+ ))} +
+ ) : ( +
暂无风险记录
+ )} +
+
From 62e8039138a4678fd8605eadb46402612a6c237c 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: Sat, 13 Sep 2025 16:14:02 +0800 Subject: [PATCH 04/18] =?UTF-8?q?fix(=E5=BE=AE=E4=BF=A1=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E8=AF=A6=E6=83=85):=20=E5=B0=86=E9=99=90=E5=88=B6=E7=AD=89?= =?UTF-8?q?=E7=BA=A7=E5=AD=97=E6=AE=B5=E4=BB=8E=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=94=B9=E4=B8=BA=E6=95=B0=E5=AD=97=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改微信账号详情页面的限制等级字段类型,从字符串类型改为数字类型以匹配后端数据格式。同时更新相关颜色和文本显示的判断逻辑。 --- .../pages/mobile/mine/wechat-accounts/detail/data.ts | 2 +- .../pages/mobile/mine/wechat-accounts/detail/index.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/data.ts b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/data.ts index f2ff3f2f..20d7a2c9 100644 --- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/data.ts +++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/data.ts @@ -17,7 +17,7 @@ export interface WechatAccountSummary { }; restrictions: { id: number; - level: string; + level: number; reason: string; date: string; }[]; 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 5ca717f8..12eb5e0f 100644 --- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx @@ -193,11 +193,11 @@ const WechatAccountDetail: React.FC = () => { navigate("/scenarios"); }; - const getRestrictionLevelColor = (level: string) => { + const getRestrictionLevelColor = (level: number) => { switch (level) { - case "high": + case 3: return "text-red-600"; - case "medium": + case 2: return "text-yellow-600"; default: return "text-gray-600"; @@ -519,9 +519,9 @@ const WechatAccountDetail: React.FC = () => { - {restriction.level === "high" + {restriction.level === 3 ? "高风险" - : restriction.level === "medium" + : restriction.level === 2 ? "中风险" : "低风险"} From a270b8ce3e70ed0ee9e895c332fbaa7f247f7750 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: Sat, 13 Sep 2025 17:12:16 +0800 Subject: [PATCH 05/18] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E7=94=9F=E6=88=90=E7=9A=84=E8=B5=84=E6=BA=90=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=93=88=E5=B8=8C=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/dist/.vite/manifest.json | 18 +++++++++--------- Cunkebao/dist/index.html | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cunkebao/dist/.vite/manifest.json b/Cunkebao/dist/.vite/manifest.json index 2289b6a9..23344959 100644 --- a/Cunkebao/dist/.vite/manifest.json +++ b/Cunkebao/dist/.vite/manifest.json @@ -1,9 +1,9 @@ { - "_charts-aNYyX7D2.js": { - "file": "assets/charts-aNYyX7D2.js", + "_charts-DsEZaGAW.js": { + "file": "assets/charts-DsEZaGAW.js", "name": "charts", "imports": [ - "_ui-DZwp85UP.js", + "_ui-YC29IQHT.js", "_vendor-Bq99rrm8.js" ] }, @@ -11,8 +11,8 @@ "file": "assets/ui-D0C0OGrH.css", "src": "_ui-D0C0OGrH.css" }, - "_ui-DZwp85UP.js": { - "file": "assets/ui-DZwp85UP.js", + "_ui-YC29IQHT.js": { + "file": "assets/ui-YC29IQHT.js", "name": "ui", "imports": [ "_vendor-Bq99rrm8.js" @@ -33,18 +33,18 @@ "name": "vendor" }, "index.html": { - "file": "assets/index-CCIZs36L.js", + "file": "assets/index-d8oC2PBC.js", "name": "index", "src": "index.html", "isEntry": true, "imports": [ "_vendor-Bq99rrm8.js", - "_ui-DZwp85UP.js", + "_ui-YC29IQHT.js", "_utils-Ft3ushmX.js", - "_charts-aNYyX7D2.js" + "_charts-DsEZaGAW.js" ], "css": [ - "assets/index-DRrzDMi4.css" + "assets/index-D8Ews0CA.css" ] } } \ No newline at end of file diff --git a/Cunkebao/dist/index.html b/Cunkebao/dist/index.html index 9b257a9d..693dadfc 100644 --- a/Cunkebao/dist/index.html +++ b/Cunkebao/dist/index.html @@ -11,13 +11,13 @@ - + - + - + - +
From 410a43e6a18462ccfb353c72fd534bd0719e3c09 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Sat, 13 Sep 2025 17:20:34 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=E8=A7=A6=E5=AE=A2=E5=AE=9D=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=8A=9F=E8=83=BD=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/chukebao/config/route.php | 11 +++++-- .../chukebao/controller/BaseController.php | 29 +++++++++++++++++++ .../chukebao/controller/LoginController.php | 13 +++++---- .../controller/WechatChatroomController.php | 21 ++++++++++++++ .../controller/WechatFriendController.php | 21 ++++++++++++++ ...PotentialListWithInCompanyV1Controller.php | 9 +++--- 6 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 Server/application/chukebao/controller/BaseController.php create mode 100644 Server/application/chukebao/controller/WechatChatroomController.php create mode 100644 Server/application/chukebao/controller/WechatFriendController.php diff --git a/Server/application/chukebao/config/route.php b/Server/application/chukebao/config/route.php index 6ae947a0..8e57834c 100644 --- a/Server/application/chukebao/config/route.php +++ b/Server/application/chukebao/config/route.php @@ -9,7 +9,14 @@ use think\facade\Route; Route::group('v1/', function () { Route::group('kefu/', function () { - + //好友相关 + Route::group('wechatFriend/', function () { + Route::get('list', 'app\chukebao\controller\WechatFriendController@getList'); // 获取好友列表 + }); + //好友相关 + Route::group('wechatChatroom/', function () { + Route::get('list', 'app\chukebao\controller\WechatChatroomController@getList'); // 获取好友列表 + }); }); @@ -18,7 +25,7 @@ Route::group('v1/', function () { // 客服登录 Route::group('v1/kefu', function () { - Route::post('login', 'app\chukebao\controller\LoginController@index'); // 获取好友列表 + Route::post('login', 'app\chukebao\controller\LoginController@index'); // 登录 }); diff --git a/Server/application/chukebao/controller/BaseController.php b/Server/application/chukebao/controller/BaseController.php new file mode 100644 index 00000000..2c877509 --- /dev/null +++ b/Server/application/chukebao/controller/BaseController.php @@ -0,0 +1,29 @@ +request->userInfo; + + if (!$user) { + throw new \Exception('未授权访问,缺少有效的身份凭证', 401); + } + + return $column ? $user[$column] : $user; + } +} \ No newline at end of file diff --git a/Server/application/chukebao/controller/LoginController.php b/Server/application/chukebao/controller/LoginController.php index d34c55e3..519b744a 100644 --- a/Server/application/chukebao/controller/LoginController.php +++ b/Server/application/chukebao/controller/LoginController.php @@ -2,18 +2,16 @@ namespace app\chukebao\controller; -use app\common\controller\BaseController; use app\common\util\JwtUtil; use Exception; use library\ResponseHelper; -use app\api\controller\UserController; use think\Db; - +use think\Controller; /** * 认证控制器 * 处理用户登录和身份验证 */ -class LoginController extends BaseController +class LoginController extends Controller { @@ -58,17 +56,17 @@ class LoginController extends BaseController 'password' => !empty($user['passwordLocal']) ? localDecrypt($user['passwordLocal']) : $password ]; + try { // 调用登录接口获取token $headerData = ['client:kefu-client']; if (!empty($verifySessionId) && !empty($verifyCode)){ $headerData[] = 'verifysessionid:'.$verifySessionId; - $headerData[] = 'verifycode:'.$verifyCode; + $headerData[] = 'verifycode:'.$verifyCode; } $header = setHeader($headerData, '', 'plain'); $result = requestCurl('https://s2.siyuguanli.com:9991/token', $params, 'POST', $header); $result = handleApiResponse($result); - if (isset($result['access_token']) && !empty($result['access_token'])) { $userData['kefuData']['token'] = $result; $headerData = ['client:kefu-client']; @@ -76,6 +74,9 @@ class LoginController extends BaseController $result2 = requestCurl('https://s2.siyuguanli.com:9991/api/account/self', [], 'GET', $header, 'json'); $self = handleApiResponse($result2); $userData['kefuData']['self'] = $self; + + Db::name('users')->where('id', $user['id'])->update(['passwordLocal' => localEncrypt($password),'updateTime' => time()]); + }else{ return ResponseHelper::error($result['error_description']); } diff --git a/Server/application/chukebao/controller/WechatChatroomController.php b/Server/application/chukebao/controller/WechatChatroomController.php new file mode 100644 index 00000000..cf03fd41 --- /dev/null +++ b/Server/application/chukebao/controller/WechatChatroomController.php @@ -0,0 +1,21 @@ +request->param('page', 1); + $limit = $this->request->param('limit', 10); + $accountId = $this->getUserInfo('s2_accountId'); + if (empty($accountId)){ + return ResponseHelper::error('请先登录'); + } + $list = Db::table('s2_wechat_chatroom')->where('accountId',$accountId)->order('id desc')->page($page, $limit)->select(); + return ResponseHelper::success($list); + } +} \ No newline at end of file diff --git a/Server/application/chukebao/controller/WechatFriendController.php b/Server/application/chukebao/controller/WechatFriendController.php new file mode 100644 index 00000000..aa9f0248 --- /dev/null +++ b/Server/application/chukebao/controller/WechatFriendController.php @@ -0,0 +1,21 @@ +request->param('page', 1); + $limit = $this->request->param('limit', 10); + $accountId = $this->getUserInfo('s2_accountId'); + if (empty($accountId)){ + return ResponseHelper::error('请先登录'); + } + $list = Db::table('s2_wechat_friend')->where('accountId',$accountId)->order('id desc')->page($page, $limit)->select(); + return ResponseHelper::success($list); + } +} \ No newline at end of file diff --git a/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php b/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php index 85b0944f..a6252698 100644 --- a/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php +++ b/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php @@ -155,11 +155,11 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController public function getUser() { - $userId = $this->request->param('userId', ''); + $wechatId = $this->request->param('wechatId', ''); $companyId = $this->getUserInfo('companyId'); - if (empty($userId)) { - return json_encode(['code' => 500, 'msg' => '用户id不能为空']); + if (empty($wechatId)) { + return json_encode(['code' => 500, 'msg' => '微信id不能为空']); } $total = [ @@ -175,12 +175,11 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController 'wa.nickname', 'wa.avatar', 'wa.gender', 'wa.phone', 'wa.alias']) ->join('wechat_account wa', 'p.identifier=wa.wechatId', 'left') ->order('p.id DESC') - ->where(['p.id' => $userId]) + ->where(['p.identifier' => $wechatId]) ->group('p.identifier') ->find(); $data['lastMsgTime'] = ''; - //来源 $source = Db::name('traffic_source')->alias('ts') ->field(['wa.nickname', 'wa.avatar', 'wa.gender', 'wa.phone', 'wa.wechatId', 'wa.alias', From c91e54370fa705b8d21690b23963b45cb6437b31 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, 15 Sep 2025 15:21:24 +0800 Subject: [PATCH 07/18] =?UTF-8?q?refactor(websocket/wechat):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=AE=A2=E6=9C=8D=E5=88=97=E8=A1=A8=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E5=B9=B6=E7=AE=80=E5=8C=96=E5=88=9D=E5=A7=8B=E5=8C=96=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在msgManage.ts中按在线状态排序客服列表 - 在main.ts中简化初始化逻辑,使用getAgentList和getChatroomList替代原有复杂逻辑 - 在api.ts中新增相关API接口并统一使用request2 --- Touchkebao/src/pages/pc/ckbox/weChat/api.ts | 87 +++++++++++-------- Touchkebao/src/pages/pc/ckbox/weChat/main.ts | 52 +++++------ .../src/store/module/websocket/msgManage.ts | 8 ++ 3 files changed, 81 insertions(+), 66 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts index af476f26..57f6cdf2 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts @@ -1,4 +1,5 @@ -import request from "@/api/request2"; +import request2 from "@/api/request2"; +import request from "@/api/request"; import { MessageData, ChatHistoryResponse, @@ -11,6 +12,16 @@ import { ChatSettings, } from "./data"; +//群、好友聊天记录列表 +export function getChatroomList() { + return request("/v1/kefu/wechatChatroom/list", {}, "GET"); +} + +//获取客服列表 +export function getAgentList() { + return request("/v1/kefu/customerService/list", {}, "GET"); +} + //读取聊天信息 //kf.quwanzhi.com:9991/api/WechatFriend/clearUnreadCount function jsonToQueryString(json) { @@ -30,7 +41,7 @@ export function WechatFriendAllot(params: { notifyReceiver: boolean; comment: string; }) { - return request( + return request2( "/api/wechatFriend/allot?" + jsonToQueryString(params), undefined, "PUT", @@ -39,7 +50,7 @@ export function WechatFriendAllot(params: { //获取可转移客服列表 export function getTransferableAgentList() { - return request("/api/account/myDepartmentAccountsForTransfer", {}, "GET"); + return request2("/api/account/myDepartmentAccountsForTransfer", {}, "GET"); } // 微信好友列表 @@ -47,7 +58,7 @@ export function WechatFriendRebackAllot(params: { wechatFriendId?: number; wechatChatroomId?: number; }) { - return request( + return request2( "/api/wechatFriend/rebackAllot?" + jsonToQueryString(params), undefined, "PUT", @@ -56,17 +67,17 @@ export function WechatFriendRebackAllot(params: { // 微信群列表 export function WechatGroup(params) { - return request("/api/WechatGroup/list", params, "GET"); + return request2("/api/WechatGroup/list", params, "GET"); } //获取聊天记录-1 清除未读 export function clearUnreadCount(params) { - return request("/api/WechatFriend/clearUnreadCount", params, "PUT"); + return request2("/api/WechatFriend/clearUnreadCount", params, "PUT"); } //更新配置 export function updateConfig(params) { - return request("/api/WechatFriend/updateConfig", params, "PUT"); + return request2("/api/WechatFriend/updateConfig", params, "PUT"); } //获取聊天记录-2 获取列表 export function getChatMessages(params: { @@ -78,7 +89,7 @@ export function getChatMessages(params: { Count: number; olderData: boolean; }) { - return request("/api/FriendMessage/SearchMessage", params, "GET"); + return request2("/api/FriendMessage/SearchMessage", params, "GET"); } export function getChatroomMessages(params: { wechatAccountId: number; @@ -89,12 +100,12 @@ export function getChatroomMessages(params: { Count: number; olderData: boolean; }) { - return request("/api/ChatroomMessage/SearchMessage", params, "GET"); + return request2("/api/ChatroomMessage/SearchMessage", params, "GET"); } //获取群列表 export function getGroupList(params: { prevId: number; count: number }) { - return request( + return request2( "/api/wechatChatroom/listExcludeMembersByPage?", params, "GET", @@ -103,7 +114,7 @@ export function getGroupList(params: { prevId: number; count: number }) { //获取群成员 export function getGroupMembers(params: { id: number }) { - return request( + return request2( "/api/WechatChatroom/listMembersByWechatChatroomId", params, "GET", @@ -112,7 +123,7 @@ export function getGroupMembers(params: { id: number }) { //触客宝登陆 export function loginWithToken(params: any) { - return request( + return request2( "/token", params, "POST", @@ -127,17 +138,17 @@ export function loginWithToken(params: any) { // 获取触客宝用户信息 export function getChuKeBaoUserInfo() { - return request("/api/account/self", {}, "GET"); + return request2("/api/account/self", {}, "GET"); } // 获取联系人列表 export const getContactList = (params: { prevId: number; count: number }) => { - return request("/api/wechatFriend/list", params, "GET"); + return request2("/api/wechatFriend/list", params, "GET"); }; //获取控制终端列表 export const getControlTerminalList = params => { - return request("/api/wechataccount", params, "GET"); + return request2("/api/wechataccount", params, "GET"); }; // 获取聊天历史 @@ -146,7 +157,7 @@ export const getChatHistory = ( page: number = 1, pageSize: number = 50, ): Promise => { - return request(`/v1/chats/${chatId}/messages`, { page, pageSize }, "GET"); + return request2(`/v1/chats/${chatId}/messages`, { page, pageSize }, "GET"); }; // 发送消息 @@ -155,7 +166,7 @@ export const sendMessage = ( content: string, type: MessageType = MessageType.TEXT, ): Promise => { - return request(`/v1/chats/${chatId}/messages`, { content, type }, "POST"); + return request2(`/v1/chats/${chatId}/messages`, { content, type }, "POST"); }; // 发送文件消息 @@ -167,17 +178,17 @@ export const sendFileMessage = ( const formData = new FormData(); formData.append("file", file); formData.append("type", type); - return request(`/v1/chats/${chatId}/messages/file`, formData, "POST"); + return request2(`/v1/chats/${chatId}/messages/file`, formData, "POST"); }; // 标记消息为已读 export const markMessageAsRead = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/read`, {}, "PUT"); + return request2(`/v1/messages/${messageId}/read`, {}, "PUT"); }; // 标记聊天为已读 export const markChatAsRead = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/read`, {}, "PUT"); + return request2(`/v1/chats/${chatId}/read`, {}, "PUT"); }; // 添加群组成员 @@ -185,7 +196,7 @@ export const addGroupMembers = ( groupId: string, memberIds: string[], ): Promise => { - return request(`/v1/groups/${groupId}/members`, { memberIds }, "POST"); + return request2(`/v1/groups/${groupId}/members`, { memberIds }, "POST"); }; // 移除群组成员 @@ -193,34 +204,34 @@ export const removeGroupMembers = ( groupId: string, memberIds: string[], ): Promise => { - return request(`/v1/groups/${groupId}/members`, { memberIds }, "DELETE"); + return request2(`/v1/groups/${groupId}/members`, { memberIds }, "DELETE"); }; // 获取在线状态 export const getOnlineStatus = (userId: string): Promise => { - return request(`/v1/users/${userId}/status`, {}, "GET"); + return request2(`/v1/users/${userId}/status`, {}, "GET"); }; // 获取消息状态 export const getMessageStatus = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/status`, {}, "GET"); + return request2(`/v1/messages/${messageId}/status`, {}, "GET"); }; // 上传文件 export const uploadFile = (file: File): Promise => { const formData = new FormData(); formData.append("file", file); - return request("/v1/upload", formData, "POST"); + return request2("/v1/upload", formData, "POST"); }; // 获取表情包列表 export const getEmojiList = (): Promise => { - return request("/v1/emojis", {}, "GET"); + return request2("/v1/emojis", {}, "GET"); }; // 获取快捷回复列表 export const getQuickReplies = (): Promise => { - return request("/v1/quick-replies", {}, "GET"); + return request2("/v1/quick-replies", {}, "GET"); }; // 添加快捷回复 @@ -228,49 +239,49 @@ export const addQuickReply = (data: { content: string; category: string; }): Promise => { - return request("/v1/quick-replies", data, "POST"); + return request2("/v1/quick-replies", data, "POST"); }; // 删除快捷回复 export const deleteQuickReply = (id: string): Promise => { - return request(`/v1/quick-replies/${id}`, {}, "DELETE"); + return request2(`/v1/quick-replies/${id}`, {}, "DELETE"); }; // 获取聊天设置 export const getChatSettings = (): Promise => { - return request("/v1/chat/settings", {}, "GET"); + return request2("/v1/chat/settings", {}, "GET"); }; // 更新聊天设置 export const updateChatSettings = ( settings: Partial, ): Promise => { - return request("/v1/chat/settings", settings, "PUT"); + return request2("/v1/chat/settings", settings, "PUT"); }; // 删除聊天会话 export const deleteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}`, {}, "DELETE"); + return request2(`/v1/chats/${chatId}`, {}, "DELETE"); }; // 置顶聊天会话 export const pinChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/pin`, {}, "PUT"); + return request2(`/v1/chats/${chatId}/pin`, {}, "PUT"); }; // 取消置顶聊天会话 export const unpinChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/unpin`, {}, "PUT"); + return request2(`/v1/chats/${chatId}/unpin`, {}, "PUT"); }; // 静音聊天会话 export const muteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/mute`, {}, "PUT"); + return request2(`/v1/chats/${chatId}/mute`, {}, "PUT"); }; // 取消静音聊天会话 export const unmuteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/unmute`, {}, "PUT"); + return request2(`/v1/chats/${chatId}/unmute`, {}, "PUT"); }; // 转发消息 @@ -278,10 +289,10 @@ export const forwardMessage = ( messageId: string, targetChatIds: string[], ): Promise => { - return request("/v1/messages/forward", { messageId, targetChatIds }, "POST"); + return request2("/v1/messages/forward", { messageId, targetChatIds }, "POST"); }; // 撤回消息 export const recallMessage = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/recall`, {}, "PUT"); + return request2(`/v1/messages/${messageId}/recall`, {}, "PUT"); }; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts index ef73aef6..151e8a26 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts @@ -13,6 +13,8 @@ import { getControlTerminalList, getContactList, getGroupList, + getAgentList, + getChatroomList, } from "./api"; import { useUserStore } from "@/store/module/user"; @@ -39,15 +41,7 @@ export const chatInitAPIdata = async () => { await asyncWeChatGroup(groupList); - // 提取不重复的wechatAccountId组 - const uniqueWechatAccountIds: number[] = getUniqueWechatAccountIds( - contractList, - groupList, - ); - - //获取控制终端列表 - const kfUserList: KfUserListData[] = - await getControlTerminalListByWechatAccountIds(uniqueWechatAccountIds); + const kfUserList: KfUserListData[] = await getAgentList(); //获取用户列表 await asyncKfUserList(kfUserList); @@ -64,28 +58,30 @@ export const chatInitAPIdata = async () => { v => v?.config && v.config?.chat, ); //排序功能 - const sortedSessions = [...filterUserSessions, ...filterGroupSessions].sort( - (a, b) => { - // 如果lastUpdateTime不存在,则将其排在最后 - if (!a.lastUpdateTime) return 1; - if (!b.lastUpdateTime) return -1; + // const sortedSessions = [...filterUserSessions, ...filterGroupSessions].sort( + // (a, b) => { + // // 如果lastUpdateTime不存在,则将其排在最后 + // if (!a.lastUpdateTime) return 1; + // if (!b.lastUpdateTime) return -1; - // 首先按时间降序排列(最新的在前面) - const timeCompare = - new Date(b.lastUpdateTime).getTime() - - new Date(a.lastUpdateTime).getTime(); + // // 首先按时间降序排列(最新的在前面) + // const timeCompare = + // new Date(b.lastUpdateTime).getTime() - + // new Date(a.lastUpdateTime).getTime(); - // 如果时间相同,则按未读消息数量降序排列 - if (timeCompare === 0) { - // 如果unreadCount不存在,则将其排在后面 - const aUnread = a.unreadCount || 0; - const bUnread = b.unreadCount || 0; - return bUnread - aUnread; // 未读消息多的排在前面 - } + // // 如果时间相同,则按未读消息数量降序排列 + // if (timeCompare === 0) { + // // 如果unreadCount不存在,则将其排在后面 + // const aUnread = a.unreadCount || 0; + // const bUnread = b.unreadCount || 0; + // return bUnread - aUnread; // 未读消息多的排在前面 + // } - return timeCompare; - }, - ); + // return timeCompare; + // }, + // ); + const sortedSessions = await getChatroomList(); + console.log("sortedSessions", sortedSessions); //会话数据同步 asyncChatSessions(sortedSessions); diff --git a/Touchkebao/src/store/module/websocket/msgManage.ts b/Touchkebao/src/store/module/websocket/msgManage.ts index 92ec518c..3f70d947 100644 --- a/Touchkebao/src/store/module/websocket/msgManage.ts +++ b/Touchkebao/src/store/module/websocket/msgManage.ts @@ -23,6 +23,14 @@ const messageHandlers: Record = { kfUserList.forEach(kfUser => { kfUser.isOnline = wechatAccountsAliveStatus[kfUser.id]; }); + + // 按在线状态排序,在线的排在前面 + kfUserList.sort((a, b) => { + if (a.isOnline && !b.isOnline) return -1; + if (!a.isOnline && b.isOnline) return 1; + return 0; + }); + asyncKfUserList(kfUserList); }, // 发送消息响应 From d802b9da1ff266393614e23a186d67d031ea04ac 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, 15 Sep 2025 15:42:05 +0800 Subject: [PATCH 08/18] =?UTF-8?q?feat(=E5=BE=AE=E4=BF=A1=E5=AE=A2=E6=9C=8D?= =?UTF-8?q?):=20=E6=96=B0=E5=A2=9E=E5=BE=AE=E4=BF=A1=E5=A5=BD=E5=8F=8B?= =?UTF-8?q?=E5=92=8C=E7=BE=A4=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=9D=E5=A7=8B=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加获取微信好友列表和群列表的API接口 - 移除旧的初始化联系人列表逻辑,改用新接口 - 清理不再使用的会话过滤和排序代码 --- Touchkebao/src/pages/pc/ckbox/weChat/api.ts | 10 +++++++ .../src/pages/pc/ckbox/weChat/index.tsx | 28 +++++++++---------- Touchkebao/src/pages/pc/ckbox/weChat/main.ts | 17 +++++------ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts index 57f6cdf2..cef0df31 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts @@ -12,6 +12,16 @@ import { ChatSettings, } from "./data"; +// 好友列表 +export function getWechatFriendList() { + return request("/v1/kefu/wechatFriend/list", {}, "GET"); +} + +// 群列表 +export function getWechatChatroomList() { + return request("/v1/kefu/wechatChatroom/list", {}, "GET"); +} + //群、好友聊天记录列表 export function getChatroomList() { return request("/v1/kefu/wechatChatroom/list", {}, "GET"); diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx index f3d6e786..88255d2c 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx @@ -22,21 +22,21 @@ const CkboxPage: React.FC = () => { setLoading(true); chatInitAPIdata() .then(response => { - const data = response as { - contractList: any[]; - groupList: any[]; - kfUserList: KfUserListData[]; - newContractList: { groupName: string; contacts: any[] }[]; - }; - const { contractList } = data; + // const data = response as { + // contractList: any[]; + // groupList: any[]; + // kfUserList: KfUserListData[]; + // newContractList: { groupName: string; contacts: any[] }[]; + // }; + // const { contractList } = data; - //找出已经在聊天的 - const isChatList = contractList.filter( - v => (v?.config && v.config?.chat) || false, - ); - isChatList.forEach(v => { - addChatSession(v); - }); + // //找出已经在聊天的 + // const isChatList = contractList.filter( + // v => (v?.config && v.config?.chat) || false, + // ); + // isChatList.forEach(v => { + // addChatSession(v); + // }); // 数据加载完成后初始化WebSocket连接 initSocket(); diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts index 151e8a26..1caef8a0 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts @@ -15,6 +15,8 @@ import { getGroupList, getAgentList, getChatroomList, + getWechatFriendList, + getWechatChatroomList, } from "./api"; import { useUserStore } from "@/store/module/user"; @@ -31,7 +33,7 @@ const { login2 } = useUserStore.getState(); export const chatInitAPIdata = async () => { try { //获取联系人列表 - const contractList = await getAllContactList(); + const contractList = await getWechatFriendList(); //获取联系人列表 asyncContractList(contractList); @@ -51,12 +53,12 @@ export const chatInitAPIdata = async () => { await asyncCountLables(countLables); //获取消息会话列表并按lastUpdateTime排序 - const filterUserSessions = contractList?.filter( - v => v?.config && v.config?.chat, - ); - const filterGroupSessions = groupList?.filter( - v => v?.config && v.config?.chat, - ); + // const filterUserSessions = contractList?.filter( + // v => v?.config && v.config?.chat, + // ); + // const filterGroupSessions = groupList?.filter( + // v => v?.config && v.config?.chat, + // ); //排序功能 // const sortedSessions = [...filterUserSessions, ...filterGroupSessions].sort( // (a, b) => { @@ -81,7 +83,6 @@ export const chatInitAPIdata = async () => { // }, // ); const sortedSessions = await getChatroomList(); - console.log("sortedSessions", sortedSessions); //会话数据同步 asyncChatSessions(sortedSessions); From c88431b564cfa4a29fe70f04cc8a3f839aa3e9c6 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, 15 Sep 2025 18:03:14 +0800 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4Cunkebao?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E4=B8=AD=E6=9C=AA=E4=BD=BF=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E8=81=8A=E5=A4=A9=E7=9B=B8=E5=85=B3=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=92=8C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构Touchkebao项目中的微信API调用逻辑,优化消息记录处理 --- Cunkebao/src/pages/pc/ckbox/api.ts | 246 ---- .../ckbox/components/NavCommon/index.data.ts | 44 - .../components/NavCommon/index.module.scss | 316 ----- .../pc/ckbox/components/NavCommon/index.tsx | 143 -- .../pc/ckbox/dashboard/index.module.scss | 193 --- .../src/pages/pc/ckbox/dashboard/index.tsx | 476 ------- Cunkebao/src/pages/pc/ckbox/data.ts | 323 ----- Cunkebao/src/pages/pc/ckbox/index.module.scss | 198 --- Cunkebao/src/pages/pc/ckbox/index.tsx | 18 - Cunkebao/src/pages/pc/ckbox/main.ts | 307 ----- Cunkebao/src/pages/pc/ckbox/weChat/api.ts | 287 ---- .../ChatWindow/ChatWindow.module.scss | 404 ------ .../MessageEnter/MessageEnter.module.scss | 181 --- .../components/chatRecord/index.tsx | 150 --- .../components/toContract/index.tsx | 253 ---- .../components/MessageEnter/index.tsx | 291 ---- .../MessageRecord/MessageRecord.module.scss | 584 -------- .../AudioMessage/AudioMessage.module.scss | 139 -- .../components/AudioMessage/AudioMessage.tsx | 230 ---- .../SmallProgramMessage.module.scss | 315 ----- .../components/SmallProgramMessage/index.tsx | 259 ---- .../VideoMessage/VideoMessage.module.scss | 153 --- .../components/VideoMessage/index.tsx | 182 --- .../components/MessageRecord/index.tsx | 588 -------- .../components/ProfileCard/Person.module.scss | 243 ---- .../components/ProfileCard/index.tsx | 1199 ----------------- .../weChat/components/ChatWindow/demo.tsx | 669 --------- .../weChat/components/ChatWindow/index.tsx | 108 -- .../components/NavCommon/index.module.scss | 258 ---- .../weChat/components/NavCommon/index.tsx | 120 -- .../MessageList/MessageList.module.scss | 156 --- .../components/SidebarMenu/MessageList/api.ts | 6 - .../SidebarMenu/MessageList/data.ts | 48 - .../SidebarMenu/MessageList/index.tsx | 97 -- .../SidebarMenu/SidebarMenu.module.scss | 111 -- .../WechatFriends/WechatFriends.module.scss | 122 -- .../SidebarMenu/WechatFriends/index.tsx | 237 ---- .../weChat/components/SidebarMenu/index.tsx | 152 --- .../components/Skeleton/index.module.scss | 185 --- .../weChat/components/Skeleton/index.tsx | 210 --- .../VerticalUserList.module.scss | 83 -- .../components/VerticalUserList/index.tsx | 65 - Cunkebao/src/pages/pc/ckbox/weChat/data.ts | 323 ----- .../pages/pc/ckbox/weChat/index.module.scss | 198 --- Cunkebao/src/pages/pc/ckbox/weChat/index.tsx | 89 -- Cunkebao/src/pages/pc/ckbox/weChat/main.ts | 307 ----- Cunkebao/src/router/module/ckbox.tsx | 27 - Touchkebao/src/pages/pc/ckbox/weChat/api.ts | 12 +- .../components/MessageRecord/index.tsx | 45 +- Touchkebao/src/pages/pc/ckbox/weChat/main.ts | 70 +- 50 files changed, 79 insertions(+), 11341 deletions(-) delete mode 100644 Cunkebao/src/pages/pc/ckbox/api.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.data.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/dashboard/index.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/dashboard/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/data.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/index.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/main.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/api.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/ChatWindow.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/chatRecord/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/MessageRecord.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/AudioMessage/AudioMessage.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/AudioMessage/AudioMessage.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/SmallProgramMessage/SmallProgramMessage.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/SmallProgramMessage/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/VideoMessage/VideoMessage.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/VideoMessage/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/MessageList.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/api.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/data.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/SidebarMenu.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/WechatFriends.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/Skeleton/index.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/Skeleton/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/VerticalUserList.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/data.ts delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/index.module.scss delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/index.tsx delete mode 100644 Cunkebao/src/pages/pc/ckbox/weChat/main.ts delete mode 100644 Cunkebao/src/router/module/ckbox.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/api.ts b/Cunkebao/src/pages/pc/ckbox/api.ts deleted file mode 100644 index 98bc336a..00000000 --- a/Cunkebao/src/pages/pc/ckbox/api.ts +++ /dev/null @@ -1,246 +0,0 @@ -import request from "@/api/request2"; -import { - MessageData, - ChatHistoryResponse, - MessageType, - OnlineStatus, - MessageStatus, - FileUploadResponse, - EmojiData, - QuickReply, - ChatSettings, -} from "./data"; - -//读取聊天信息 -//kf.quwanzhi.com:9991/api/WechatFriend/clearUnreadCount - -export function WechatGroup(params) { - return request("/api/WechatGroup/list", params, "GET"); -} - -//获取聊天记录-1 清除未读 -export function clearUnreadCount(params) { - return request("/api/WechatFriend/clearUnreadCount", params, "PUT"); -} - -//更新配置 -export function updateConfig(params) { - return request("/api/WechatFriend/updateConfig", params, "PUT"); -} -//获取聊天记录-2 获取列表 -export function getChatMessages(params: { - wechatAccountId: number; - wechatFriendId?: number; - wechatChatroomId?: number; - From: number; - To: number; - Count: number; - olderData: boolean; -}) { - return request("/api/FriendMessage/SearchMessage", params, "GET"); -} -export function getChatroomMessages(params: { - wechatAccountId: number; - wechatFriendId?: number; - wechatChatroomId?: number; - From: number; - To: number; - Count: number; - olderData: boolean; -}) { - return request("/api/ChatroomMessage/SearchMessage", params, "GET"); -} - -//获取群列表 -export function getGroupList(params: { prevId: number; count: number }) { - return request( - "/api/wechatChatroom/listExcludeMembersByPage?", - params, - "GET", - ); -} - -//获取群成员 -export function getGroupMembers(params: { id: number }) { - return request( - "/api/WechatChatroom/listMembersByWechatChatroomId", - params, - "GET", - ); -} - -//触客宝登陆 -export function loginWithToken(params: any) { - return request( - "/token", - params, - "POST", - { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }, - 1000, - ); -} - -// 获取触客宝用户信息 -export function getChuKeBaoUserInfo() { - return request("/api/account/self", {}, "GET"); -} - -// 获取联系人列表 -export const getContactList = (params: { prevId: number; count: number }) => { - return request("/api/wechatFriend/list", params, "GET"); -}; - -//获取控制终端列表 -export const getControlTerminalList = params => { - return request("/api/wechataccount", params, "GET"); -}; - -// 获取聊天历史 -export const getChatHistory = ( - chatId: string, - page: number = 1, - pageSize: number = 50, -): Promise => { - return request(`/v1/chats/${chatId}/messages`, { page, pageSize }, "GET"); -}; - -// 发送消息 -export const sendMessage = ( - chatId: string, - content: string, - type: MessageType = MessageType.TEXT, -): Promise => { - return request(`/v1/chats/${chatId}/messages`, { content, type }, "POST"); -}; - -// 发送文件消息 -export const sendFileMessage = ( - chatId: string, - file: File, - type: MessageType, -): Promise => { - const formData = new FormData(); - formData.append("file", file); - formData.append("type", type); - return request(`/v1/chats/${chatId}/messages/file`, formData, "POST"); -}; - -// 标记消息为已读 -export const markMessageAsRead = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/read`, {}, "PUT"); -}; - -// 标记聊天为已读 -export const markChatAsRead = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/read`, {}, "PUT"); -}; - -// 添加群组成员 -export const addGroupMembers = ( - groupId: string, - memberIds: string[], -): Promise => { - return request(`/v1/groups/${groupId}/members`, { memberIds }, "POST"); -}; - -// 移除群组成员 -export const removeGroupMembers = ( - groupId: string, - memberIds: string[], -): Promise => { - return request(`/v1/groups/${groupId}/members`, { memberIds }, "DELETE"); -}; - -// 获取在线状态 -export const getOnlineStatus = (userId: string): Promise => { - return request(`/v1/users/${userId}/status`, {}, "GET"); -}; - -// 获取消息状态 -export const getMessageStatus = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/status`, {}, "GET"); -}; - -// 上传文件 -export const uploadFile = (file: File): Promise => { - const formData = new FormData(); - formData.append("file", file); - return request("/v1/upload", formData, "POST"); -}; - -// 获取表情包列表 -export const getEmojiList = (): Promise => { - return request("/v1/emojis", {}, "GET"); -}; - -// 获取快捷回复列表 -export const getQuickReplies = (): Promise => { - return request("/v1/quick-replies", {}, "GET"); -}; - -// 添加快捷回复 -export const addQuickReply = (data: { - content: string; - category: string; -}): Promise => { - return request("/v1/quick-replies", data, "POST"); -}; - -// 删除快捷回复 -export const deleteQuickReply = (id: string): Promise => { - return request(`/v1/quick-replies/${id}`, {}, "DELETE"); -}; - -// 获取聊天设置 -export const getChatSettings = (): Promise => { - return request("/v1/chat/settings", {}, "GET"); -}; - -// 更新聊天设置 -export const updateChatSettings = ( - settings: Partial, -): Promise => { - return request("/v1/chat/settings", settings, "PUT"); -}; - -// 删除聊天会话 -export const deleteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}`, {}, "DELETE"); -}; - -// 置顶聊天会话 -export const pinChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/pin`, {}, "PUT"); -}; - -// 取消置顶聊天会话 -export const unpinChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/unpin`, {}, "PUT"); -}; - -// 静音聊天会话 -export const muteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/mute`, {}, "PUT"); -}; - -// 取消静音聊天会话 -export const unmuteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/unmute`, {}, "PUT"); -}; - -// 转发消息 -export const forwardMessage = ( - messageId: string, - targetChatIds: string[], -): Promise => { - return request("/v1/messages/forward", { messageId, targetChatIds }, "POST"); -}; - -// 撤回消息 -export const recallMessage = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/recall`, {}, "PUT"); -}; diff --git a/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.data.ts b/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.data.ts deleted file mode 100644 index f42e11af..00000000 --- a/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.data.ts +++ /dev/null @@ -1,44 +0,0 @@ -// 菜单项接口 -export interface MenuItem { - id: string; - title: string; - icon: string; - path?: string; -} - -// 菜单列表数据 -export const menuList: MenuItem[] = [ - { - id: "dashboard", - title: "数据面板", - icon: "📊", - path: "/ckbox/dashboard", - }, - { - id: "wechat", - title: "微信管理", - icon: "💬", - path: "/ckbox/weChat", - }, -]; - -// 抽屉菜单配置数据 -export const drawerMenuData = { - header: { - logoIcon: "✨", - appName: "触客宝", - appDesc: "AI智能营销系统", - }, - primaryButton: { - title: "AI智能客服", - icon: "🔒", - }, - footer: { - balanceIcon: "⚡", - balanceLabel: "算力余额", - balanceValue: "9307.423", - }, -}; - -// 导出默认配置 -export default drawerMenuData; diff --git a/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.module.scss b/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.module.scss deleted file mode 100644 index 6bf58b79..00000000 --- a/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.module.scss +++ /dev/null @@ -1,316 +0,0 @@ -.header { - background: #fff; - padding: 0 16px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - display: flex; - align-items: center; - justify-content: space-between; - height: 64px; - border-bottom: 1px solid #f0f0f0; -} - -.headerLeft { - display: flex; - align-items: center; - gap: 12px; -} - -.menuButton { - display: flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - border-radius: 6px; - transition: all 0.3s; - - &:hover { - background-color: #f5f5f5; - } - - .anticon { - font-size: 18px; - color: #666; - } -} - -.title { - font-size: 18px; - color: #333; - margin: 0; -} - -.headerRight { - display: flex; - align-items: center; - gap: 16px; -} - -.userInfo { - display: flex; - align-items: center; - gap: 16px; - padding: 8px 0; - - .suanli { - display: flex; - align-items: center; - gap: 4px; - font-size: 14px; - color: #666; - font-weight: 500; - padding: 6px 12px; - background: #f8f9fa; - border-radius: 20px; - border: 1px solid #e9ecef; - - .suanliIcon { - font-size: 16px; - color: #ffc107; - } - } -} - -.messageButton { - display: flex; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - border-radius: 50%; - transition: all 0.3s; - border: 1px solid #e9ecef; - background: #fff; - - &:hover { - background-color: #f8f9fa; - border-color: #1890ff; - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15); - } - - .anticon { - font-size: 18px; - color: #666; - } -} - -.userSection { - display: flex; - align-items: center; - gap: 12px; - cursor: pointer; - padding: 8px 16px; - border-radius: 24px; - transition: all 0.3s; - border: 1px solid #e9ecef; - background: #fff; - - &:hover { - background-color: #f8f9fa; - border-color: #1890ff; - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15); - } -} - -.userNickname { - font-size: 14px; - color: #333; - font-weight: 600; - max-width: 100px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.avatar { - border: 2px solid #e9ecef; - transition: all 0.3s; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -} - -.username { - font-size: 14px; - color: #333; - font-weight: 500; - margin-left: 8px; -} - -// 抽屉样式 -.drawer { - :global(.ant-drawer-header) { - background: #fafafa; - border-bottom: 1px solid #f0f0f0; - } - - :global(.ant-drawer-body) { - padding: 0; - } -} - -.drawerContent { - height: 100%; - display: flex; - flex-direction: column; - background: #f8f9fa; -} - -.drawerHeader { - padding: 20px; - background: #fff; - border-bottom: none; -} - -.logoSection { - display: flex; - align-items: center; - gap: 12px; -} - -.logoIcon { - width: 48px; - height: 48px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - font-size: 24px; - color: white; -} - -.logoText { - display: flex; - flex-direction: column; -} - -.appName { - font-size: 18px; - font-weight: 600; - color: #333; - margin: 0; -} - -.appDesc { - font-size: 12px; - color: #999; - margin: 2px 0 0 0; -} - -.drawerBody { - flex: 1; - padding: 20px; - display: flex; - flex-direction: column; - gap: 16px; -} - -.primaryButton { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 12px; - padding: 16px 20px; - display: flex; - align-items: center; - gap: 12px; - cursor: pointer; - transition: all 0.3s; - - &:hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); - } - - .buttonIcon { - font-size: 20px; - } - - span { - font-size: 16px; - font-weight: 600; - color: white; - } -} - -.menuSection { - margin-top: 8px; -} - -.menuItem { - display: flex; - align-items: center; - padding: 16px 20px; - cursor: pointer; - transition: all 0.3s; - border-radius: 8px; - - &:hover { - background-color: #f0f0f0; - } - - span { - font-size: 16px; - color: #333; - font-weight: 500; - } -} - -.menuIcon { - font-size: 20px; - margin-right: 12px; - width: 20px; - text-align: center; -} - -.drawerFooter { - padding: 20px; - background: #fff; - border-top: 1px solid #f0f0f0; - .balanceSection { - display: flex; - justify-content: space-between; - align-items: center; - .balanceIcon { - color: #666; - .suanliIcon { - font-size: 20px; - } - } - - .balanceText { - color: #3d9c0d; - } - } -} - -.balanceLabel { - font-size: 12px; - color: #999; - margin: 0; -} - -.balanceAmount { - font-size: 18px; - font-weight: 600; - color: #52c41a; - margin: 2px 0 0 0; -} - -// 响应式设计 -@media (max-width: 768px) { - .header { - padding: 0 12px; - } - - .title { - font-size: 16px; - } - - .username { - display: none; - } - - .drawer { - width: 280px !important; - } -} diff --git a/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx deleted file mode 100644 index 500099fa..00000000 --- a/Cunkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import React, { useState } from "react"; -import { Layout, Drawer, Avatar, Dropdown, Space, Button } from "antd"; -import { - MenuOutlined, - UserOutlined, - LogoutOutlined, - SettingOutlined, -} from "@ant-design/icons"; -import type { MenuProps } from "antd"; -import { useCkChatStore } from "@/store/module/ckchat/ckchat"; -import { useNavigate } from "react-router-dom"; -import { drawerMenuData, menuList } from "./index.data"; -import styles from "./index.module.scss"; - -const { Header } = Layout; - -interface NavCommonProps { - title?: string; - onMenuClick?: () => void; -} - -const NavCommon: React.FC = ({ - title = "触客宝", - onMenuClick, -}) => { - const [drawerVisible, setDrawerVisible] = useState(false); - const navigate = useNavigate(); - const { userInfo } = useCkChatStore(); - - // 处理菜单图标点击 - const handleMenuClick = () => { - setDrawerVisible(true); - onMenuClick?.(); - }; - - // 处理抽屉关闭 - const handleDrawerClose = () => { - setDrawerVisible(false); - }; - - // 默认抽屉内容 - const defaultDrawerContent = ( -
-
-
-
- {drawerMenuData.header.logoIcon} -
-
-
- {drawerMenuData.header.appName} -
-
- {drawerMenuData.header.appDesc} -
-
-
-
-
-
-
- {drawerMenuData.primaryButton.icon} -
- {drawerMenuData.primaryButton.title} -
-
- {menuList.map((item, index) => ( -
{ - if (item.path) { - navigate(item.path); - setDrawerVisible(false); - } - }} - > -
{item.icon}
- {item.title} -
- ))} -
-
-
-
-
- - {drawerMenuData.footer.balanceIcon} - - {drawerMenuData.footer.balanceLabel} -
-
- {drawerMenuData.footer.balanceValue} -
-
-
-
- ); - - return ( - <> -
-
-
- -
- - - - 9307.423 - - } - src={userInfo?.account?.avatar} - className={styles.avatar} - /> - -
-
- - - {defaultDrawerContent} - - - ); -}; - -export default NavCommon; diff --git a/Cunkebao/src/pages/pc/ckbox/dashboard/index.module.scss b/Cunkebao/src/pages/pc/ckbox/dashboard/index.module.scss deleted file mode 100644 index 96fdbab6..00000000 --- a/Cunkebao/src/pages/pc/ckbox/dashboard/index.module.scss +++ /dev/null @@ -1,193 +0,0 @@ -.monitoring { - padding: 24px; - background-color: #f5f5f5; - min-height: 100vh; - - .header { - margin-bottom: 24px; - - h2 { - margin: 0 0 8px 0; - color: #262626; - font-size: 24px; - font-weight: 600; - } - - p { - margin: 0; - color: #8c8c8c; - font-size: 14px; - } - } - - .statsRow { - margin-bottom: 24px; - - .statCard { - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - transition: all 0.3s ease; - - &:hover { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); - transform: translateY(-2px); - } - } - } - - .progressRow { - margin-bottom: 24px; - - .progressCard, - .metricsCard { - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - height: 280px; - - .ant-card-body { - height: calc(100% - 57px); - display: flex; - flex-direction: column; - justify-content: space-between; - } - } - - .progressItem { - margin-bottom: 20px; - - &:last-child { - margin-bottom: 0; - } - - span { - display: block; - margin-bottom: 8px; - color: #595959; - font-size: 14px; - } - } - - .metricItem { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 0; - border-bottom: 1px solid #f0f0f0; - - &:last-child { - border-bottom: none; - } - - span { - color: #595959; - font-size: 14px; - } - - .metricValue { - display: flex; - align-items: center; - gap: 8px; - - span { - font-weight: 600; - font-size: 16px; - color: #262626; - } - } - } - } - - .chartsRow { - margin-top: 24px; - } - - .chartCard { - .ant-card-head { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - color: white; - - .ant-card-head-title { - color: white; - font-weight: 600; - } - } - - .ant-card-body { - padding: 20px; - } - - // 图表容器样式 - .g2-tooltip { - background: rgba(0, 0, 0, 0.8); - border-radius: 6px; - color: white; - } - } - - .tableRow { - margin-top: 24px; - } - - .tableCard { - .ant-card-head { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - - .ant-card-head-title { - color: white; - } - } - } - - .tableCard { - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - - .ant-table { - .ant-table-thead > tr > th { - background-color: #fafafa; - border-bottom: 1px solid #f0f0f0; - font-weight: 600; - } - - .ant-table-tbody > tr { - &:hover { - background-color: #f5f5f5; - } - } - } - } -} - -// 响应式设计 -@media (max-width: 768px) { - .monitoring { - padding: 16px; - - .header { - h2 { - font-size: 20px; - } - } - - .progressRow { - .progressCard, - .metricsCard { - height: auto; - margin-bottom: 16px; - } - } - } -} - -@media (max-width: 576px) { - .monitoring { - padding: 12px; - - .statsRow { - .statCard { - margin-bottom: 12px; - } - } - } -} \ No newline at end of file diff --git a/Cunkebao/src/pages/pc/ckbox/dashboard/index.tsx b/Cunkebao/src/pages/pc/ckbox/dashboard/index.tsx deleted file mode 100644 index b6f0c923..00000000 --- a/Cunkebao/src/pages/pc/ckbox/dashboard/index.tsx +++ /dev/null @@ -1,476 +0,0 @@ -import React from "react"; -import { Card, Row, Col, Statistic, Progress, Table, Tag } from "antd"; -import { - UserOutlined, - MessageOutlined, - TeamOutlined, - TrophyOutlined, - ArrowUpOutlined, - ArrowDownOutlined, -} from "@ant-design/icons"; -import * as echarts from "echarts"; -import ReactECharts from "echarts-for-react"; -import styles from "./index.module.scss"; - -interface DashboardProps { - // 预留接口属性 -} - -const Dashboard: React.FC = () => { - // 模拟数据 - const statsData = [ - { - title: "在线设备数", - value: 128, - prefix: , - suffix: "台", - valueStyle: { color: "#3f8600" }, - }, - { - title: "今日消息量", - value: 2456, - prefix: , - suffix: "条", - valueStyle: { color: "#1890ff" }, - }, - { - title: "活跃群组", - value: 89, - prefix: , - suffix: "个", - valueStyle: { color: "#722ed1" }, - }, - { - title: "成功率", - value: 98.5, - prefix: , - suffix: "%", - valueStyle: { color: "#f5222d" }, - }, - ]; - - const tableColumns = [ - { - title: "设备名称", - dataIndex: "deviceName", - key: "deviceName", - }, - { - title: "状态", - dataIndex: "status", - key: "status", - render: (status: string) => ( - {status} - ), - }, - { - title: "消息数", - dataIndex: "messageCount", - key: "messageCount", - }, - { - title: "最后活跃时间", - dataIndex: "lastActive", - key: "lastActive", - }, - ]; - - const tableData = [ - { - key: "1", - deviceName: "设备001", - status: "在线", - messageCount: 245, - lastActive: "2024-01-15 14:30:25", - }, - { - key: "2", - deviceName: "设备002", - status: "离线", - messageCount: 156, - lastActive: "2024-01-15 12:15:10", - }, - { - key: "3", - deviceName: "设备003", - status: "在线", - messageCount: 389, - lastActive: "2024-01-15 14:28:45", - }, - ]; - - // 图表数据 - const lineData = [ - { time: "00:00", value: 120 }, - { time: "02:00", value: 132 }, - { time: "04:00", value: 101 }, - { time: "06:00", value: 134 }, - { time: "08:00", value: 190 }, - { time: "10:00", value: 230 }, - { time: "12:00", value: 210 }, - { time: "14:00", value: 220 }, - { time: "16:00", value: 165 }, - { time: "18:00", value: 127 }, - { time: "20:00", value: 82 }, - { time: "22:00", value: 91 }, - ]; - - const columnData = [ - { type: "消息发送", value: 27 }, - { type: "消息接收", value: 25 }, - { type: "群组管理", value: 18 }, - { type: "设备监控", value: 15 }, - { type: "数据同步", value: 10 }, - { type: "其他", value: 5 }, - ]; - - const pieData = [ - { type: "在线设备", value: 128 }, - { type: "离线设备", value: 32 }, - { type: "维护中", value: 8 }, - ]; - - const areaData = [ - { time: "1月", value: 3000 }, - { time: "2月", value: 4000 }, - { time: "3月", value: 3500 }, - { time: "4月", value: 5000 }, - { time: "5月", value: 4900 }, - { time: "6月", value: 6000 }, - ]; - - // ECharts配置 - const lineOption = { - title: { - text: "24小时消息趋势", - left: "center", - textStyle: { - color: "#333", - fontSize: 16, - }, - }, - tooltip: { - trigger: "axis", - backgroundColor: "rgba(0,0,0,0.8)", - textStyle: { - color: "#fff", - }, - }, - grid: { - left: "3%", - right: "4%", - bottom: "3%", - containLabel: true, - }, - xAxis: { - type: "category", - data: lineData.map(item => item.time), - axisLine: { - lineStyle: { - color: "#ccc", - }, - }, - }, - yAxis: { - type: "value", - axisLine: { - lineStyle: { - color: "#ccc", - }, - }, - }, - series: [ - { - data: lineData.map(item => item.value), - type: "line", - smooth: true, - lineStyle: { - color: "#1890ff", - width: 3, - }, - itemStyle: { - color: "#1890ff", - }, - areaStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 0, color: "rgba(24, 144, 255, 0.3)" }, - { offset: 1, color: "rgba(24, 144, 255, 0.1)" }, - ]), - }, - }, - ], - }; - - const columnOption = { - title: { - text: "功能使用分布", - left: "center", - textStyle: { - color: "#333", - fontSize: 16, - }, - }, - tooltip: { - trigger: "axis", - backgroundColor: "rgba(0,0,0,0.8)", - textStyle: { - color: "#fff", - }, - }, - grid: { - left: "3%", - right: "4%", - bottom: "3%", - containLabel: true, - }, - xAxis: { - type: "category", - data: columnData.map(item => item.type), - axisLabel: { - rotate: 45, - }, - axisLine: { - lineStyle: { - color: "#ccc", - }, - }, - }, - yAxis: { - type: "value", - axisLine: { - lineStyle: { - color: "#ccc", - }, - }, - }, - series: [ - { - data: columnData.map(item => item.value), - type: "bar", - itemStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 0, color: "#52c41a" }, - { offset: 1, color: "#389e0d" }, - ]), - }, - }, - ], - }; - - const pieOption = { - title: { - text: "设备状态分布", - left: "center", - textStyle: { - color: "#333", - fontSize: 16, - }, - }, - tooltip: { - trigger: "item", - backgroundColor: "rgba(0,0,0,0.8)", - textStyle: { - color: "#fff", - }, - }, - legend: { - orient: "vertical", - left: "left", - }, - series: [ - { - name: "设备状态", - type: "pie", - radius: "50%", - data: pieData.map(item => ({ name: item.type, value: item.value })), - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: "rgba(0, 0, 0, 0.5)", - }, - }, - itemStyle: { - borderRadius: 5, - borderColor: "#fff", - borderWidth: 2, - }, - }, - ], - }; - - const areaOption = { - title: { - text: "月度数据趋势", - left: "center", - textStyle: { - color: "#333", - fontSize: 16, - }, - }, - tooltip: { - trigger: "axis", - backgroundColor: "rgba(0,0,0,0.8)", - textStyle: { - color: "#fff", - }, - }, - grid: { - left: "3%", - right: "4%", - bottom: "3%", - containLabel: true, - }, - xAxis: { - type: "category", - data: areaData.map(item => item.time), - axisLine: { - lineStyle: { - color: "#ccc", - }, - }, - }, - yAxis: { - type: "value", - axisLine: { - lineStyle: { - color: "#ccc", - }, - }, - }, - series: [ - { - data: areaData.map(item => item.value), - type: "line", - smooth: true, - lineStyle: { - color: "#722ed1", - width: 3, - }, - itemStyle: { - color: "#722ed1", - }, - areaStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 0, color: "rgba(114, 46, 209, 0.6)" }, - { offset: 1, color: "rgba(114, 46, 209, 0.1)" }, - ]), - }, - }, - ], - }; - - return ( -
-
-

数据监控看板

-

实时监控系统运行状态和数据指标

-
- - {/* 统计卡片 */} - - {statsData.map((stat, index) => ( - - - - - - ))} - - - {/* 进度指标 */} - - - -
- CPU使用率 - -
-
- 内存使用率 - -
-
- 磁盘使用率 - -
-
- - - -
- 消息处理速度 -
- 1,245 - -
-
-
- 错误率 -
- 0.2% - -
-
-
- 响应时间 -
- 125ms - -
-
-
- -
- - {/* 图表区域 */} - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* 设备状态表格 */} - - - - - - - - - ); -}; - -export default Dashboard; diff --git a/Cunkebao/src/pages/pc/ckbox/data.ts b/Cunkebao/src/pages/pc/ckbox/data.ts deleted file mode 100644 index 0444a312..00000000 --- a/Cunkebao/src/pages/pc/ckbox/data.ts +++ /dev/null @@ -1,323 +0,0 @@ -// 消息列表数据接口 - 支持weChatGroup和contracts两种数据类型 -export interface MessageListData { - serverId: number | string; // 服务器ID作为主键 - id?: number; // 接口数据的原始ID字段 - - // 数据类型标识 - dataType: "weChatGroup" | "contracts"; // 数据类型:微信群组或联系人 - - // 通用字段(两种类型都有的字段) - wechatAccountId: number; // 微信账号ID - tenantId: number; // 租户ID - accountId: number; // 账号ID - nickname: string; // 昵称 - avatar?: string; // 头像 - groupId: number; // 分组ID - config?: { - chat: boolean; - }; // 配置信息 - labels?: string[]; // 标签列表 - unreadCount: number; // 未读消息数 - - // 联系人特有字段(当dataType为'contracts'时使用) - wechatId?: string; // 微信ID - alias?: string; // 别名 - conRemark?: string; // 备注 - quanPin?: string; // 全拼 - gender?: number; // 性别 - region?: string; // 地区 - addFrom?: number; // 添加来源 - phone?: string; // 电话 - signature?: string; // 签名 - extendFields?: any; // 扩展字段 - city?: string; // 城市 - lastUpdateTime?: string; // 最后更新时间 - isPassed?: boolean; // 是否通过 - thirdParty?: any; // 第三方 - additionalPicture?: string; // 附加图片 - desc?: string; // 描述 - lastMessageTime?: number; // 最后消息时间 - duplicate?: boolean; // 是否重复 - - // 微信群组特有字段(当dataType为'weChatGroup'时使用) - chatroomId?: string; // 群聊ID - chatroomOwner?: string; // 群主 - chatroomAvatar?: string; // 群头像 - notice?: string; // 群公告 - selfDisplyName?: string; // 自己在群里的显示名称 - - [key: string]: any; // 兼容其他字段 -} - -//联系人标签分组 -export interface ContactGroupByLabel { - id: number; - accountId?: number; - groupName: string; - tenantId?: number; - count: number; - [key: string]: any; -} -//终端用户数据接口 -export interface KfUserListData { - id: number; - tenantId: number; - wechatId: string; - nickname: string; - alias: string; - avatar: string; - gender: number; - region: string; - signature: string; - bindQQ: string; - bindEmail: string; - bindMobile: string; - createTime: string; - currentDeviceId: number; - isDeleted: boolean; - deleteTime: string; - groupId: number; - memo: string; - wechatVersion: string; - labels: string[]; - lastUpdateTime: string; - isOnline?: boolean; - [key: string]: any; -} - -// 账户信息接口 -export interface CkAccount { - id: number; - realName: string; - nickname: string | null; - memo: string | null; - avatar: string; - userName: string; - secret: string; - accountType: number; - departmentId: number; - useGoogleSecretKey: boolean; - hasVerifyGoogleSecret: boolean; -} - -//群聊数据接口 -export interface weChatGroup { - id?: number; - wechatAccountId: number; - tenantId: number; - accountId: number; - chatroomId: string; - chatroomOwner: string; - conRemark: string; - nickname: string; - chatroomAvatar: string; - groupId: number; - config?: { - chat: boolean; - }; - labels?: string[]; - unreadCount: number; - notice: string; - selfDisplyName: string; - wechatChatroomId: number; - serverId?: number; - [key: string]: any; -} - -// 联系人数据接口 -export interface ContractData { - id?: number; - serverId?: number; - wechatAccountId: number; - wechatId: string; - alias: string; - conRemark: string; - nickname: string; - quanPin: string; - avatar?: string; - gender: number; - region: string; - addFrom: number; - phone: string; - labels: string[]; - signature: string; - accountId: number; - extendFields: null; - city?: string; - lastUpdateTime: string; - isPassed: boolean; - tenantId: number; - groupId: number; - thirdParty: null; - additionalPicture: string; - desc: string; - config?: { - chat: boolean; - }; - lastMessageTime: number; - unreadCount: number; - duplicate: boolean; - [key: string]: any; -} - -//聊天记录接口 -export interface ChatRecord { - id: number; - wechatFriendId: number; - wechatAccountId: number; - tenantId: number; - accountId: number; - synergyAccountId: number; - content: string; - msgType: number; - msgSubType: number; - msgSvrId: string; - isSend: boolean; - createTime: string; - isDeleted: boolean; - deleteTime: string; - sendStatus: number; - wechatTime: number; - origin: number; - msgId: number; - recalled: boolean; - sender?: { - chatroomNickname: string; - isAdmin: boolean; - isDeleted: boolean; - nickname: string; - ownerWechatId: string; - wechatId: string; - [key: string]: any; - }; - [key: string]: any; -} - -/** - * 微信好友基本信息接口 - * 包含主要字段和兼容性字段 - */ -export interface WechatFriend { - // 主要字段 - id: number; // 好友ID - wechatAccountId: number; // 微信账号ID - wechatId: string; // 微信ID - nickname: string; // 昵称 - conRemark: string; // 备注名 - avatar: string; // 头像URL - gender: number; // 性别:1-男,2-女,0-未知 - region: string; // 地区 - phone: string; // 电话 - labels: string[]; // 标签列表 - [key: string]: any; -} - -// 消息类型枚举 -export enum MessageType { - TEXT = "text", - IMAGE = "image", - VOICE = "voice", - VIDEO = "video", - FILE = "file", - LOCATION = "location", -} - -// 消息数据接口 -export interface MessageData { - id: string; - senderId: string; - senderName: string; - content: string; - type: MessageType; - timestamp: string; - isRead: boolean; - replyTo?: string; - forwardFrom?: string; -} - -// 聊天会话类型 -export type ChatType = "private" | "group"; - -// 聊天会话接口 -export interface ChatSession { - id: string; - type: ChatType; - name: string; - avatar?: string; - lastMessage: string; - lastTime: string; - unreadCount: number; - online: boolean; - members?: string[]; - pinned?: boolean; - muted?: boolean; -} - -// 聊天历史响应接口 -export interface ChatHistoryResponse { - messages: MessageData[]; - hasMore: boolean; - total: number; -} - -// 发送消息请求接口 -export interface SendMessageRequest { - chatId: string; - content: string; - type: MessageType; - replyTo?: string; -} - -// 搜索联系人请求接口 -export interface SearchContactRequest { - keyword: string; - limit?: number; -} - -// 在线状态接口 -export interface OnlineStatus { - userId: string; - online: boolean; - lastSeen: string; -} - -// 消息状态接口 -export interface MessageStatus { - messageId: string; - status: "sending" | "sent" | "delivered" | "read" | "failed"; - timestamp: string; -} - -// 文件上传响应接口 -export interface FileUploadResponse { - url: string; - filename: string; - size: number; - type: string; -} - -// 表情包接口 -export interface EmojiData { - id: string; - name: string; - url: string; - category: string; -} - -// 快捷回复接口 -export interface QuickReply { - id: string; - content: string; - category: string; - useCount: number; -} - -// 聊天设置接口 -export interface ChatSettings { - autoReply: boolean; - autoReplyMessage: string; - notification: boolean; - sound: boolean; - theme: "light" | "dark"; - fontSize: "small" | "medium" | "large"; -} diff --git a/Cunkebao/src/pages/pc/ckbox/index.module.scss b/Cunkebao/src/pages/pc/ckbox/index.module.scss deleted file mode 100644 index 9b37437e..00000000 --- a/Cunkebao/src/pages/pc/ckbox/index.module.scss +++ /dev/null @@ -1,198 +0,0 @@ -.ckboxLayout { - height: 100vh; - background: #fff; - display: flex; - flex-direction: column; - - .header { - background: #1890ff; - color: #fff; - height: 64px; - line-height: 64px; - padding: 0 24px; - font-size: 18px; - font-weight: bold; - } - - .verticalSider { - background: #2e2e2e; - border-right: 1px solid #f0f0f0; - overflow: hidden; - } - - .sider { - background: #fff; - border-right: 1px solid #f0f0f0; - overflow: auto; - } - - .sidebar { - height: 100%; - display: flex; - flex-direction: column; - - .searchBar { - padding: 16px; - border-bottom: 1px solid #f0f0f0; - background: #fff; - - :global(.ant-input) { - border-radius: 20px; - background: #f5f5f5; - border: none; - - &:focus { - background: #fff; - border: 1px solid #1890ff; - box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); - } - } - } - - .tabs { - flex: 1; - display: flex; - flex-direction: column; - - :global(.ant-tabs-content) { - flex: 1; - overflow: hidden; - } - - :global(.ant-tabs-tabpane) { - height: 100%; - overflow: hidden; - } - - :global(.ant-tabs-nav) { - margin: 0; - padding: 0 16px; - background: #fff; - border-bottom: 1px solid #f0f0f0; - - :global(.ant-tabs-tab) { - padding: 12px 16px; - margin: 0; - - &:hover { - color: #1890ff; - } - - &.ant-tabs-tab-active { - .ant-tabs-tab-btn { - color: #1890ff; - } - } - } - - :global(.ant-tabs-ink-bar) { - background: #1890ff; - } - } - } - - .emptyState { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 200px; - color: #8c8c8c; - - p { - margin-top: 16px; - font-size: 14px; - } - } - } - - .mainContent { - background: #f5f5f5; - display: flex; - flex-direction: column; - overflow: auto; - - .chatContainer { - height: 100%; - display: flex; - flex-direction: column; - - .chatToolbar { - background: #fff; - border-bottom: 1px solid #f0f0f0; - padding: 8px 16px; - display: flex; - justify-content: flex-end; - align-items: center; - min-height: 48px; - } - } - - .welcomeScreen { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - background: #fff; - - .welcomeContent { - text-align: center; - color: #8c8c8c; - - h2 { - margin: 24px 0 12px 0; - color: #262626; - font-size: 24px; - font-weight: 600; - } - - p { - font-size: 16px; - margin: 0; - } - } - } - } -} - -// 响应式设计 -@media (max-width: 768px) { - .ckboxLayout { - .sidebar { - .searchBar { - padding: 12px; - } - - .tabs { - :global(.ant-tabs-nav) { - padding: 0 12px; - - :global(.ant-tabs-tab) { - padding: 10px 12px; - } - } - } - } - - .mainContent { - .chatContainer { - .chatToolbar { - padding: 6px 12px; - min-height: 40px; - } - } - - .welcomeScreen { - .welcomeContent { - h2 { - font-size: 20px; - } - - p { - font-size: 14px; - } - } - } - } - } -} diff --git a/Cunkebao/src/pages/pc/ckbox/index.tsx b/Cunkebao/src/pages/pc/ckbox/index.tsx deleted file mode 100644 index 2ff2022a..00000000 --- a/Cunkebao/src/pages/pc/ckbox/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; -import Layout from "@/components/Layout/Layout"; -import { Outlet } from "react-router-dom"; -import NavCommon from "./components/NavCommon"; -import styles from "./index.module.scss"; -const CkboxPage: React.FC = () => { - return ( - - } - > - - - ); -}; - -export default CkboxPage; diff --git a/Cunkebao/src/pages/pc/ckbox/main.ts b/Cunkebao/src/pages/pc/ckbox/main.ts deleted file mode 100644 index 19f6cf62..00000000 --- a/Cunkebao/src/pages/pc/ckbox/main.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { - asyncKfUserList, - asyncContractList, - asyncChatSessions, - asyncWeChatGroup, - asyncCountLables, - useCkChatStore, -} from "@/store/module/ckchat/ckchat"; -import { useWebSocketStore } from "@/store/module/websocket/websocket"; - -import { - loginWithToken, - getControlTerminalList, - getContactList, - getGroupList, -} from "./api"; - -import { useUserStore } from "@/store/module/user"; - -import { - KfUserListData, - ContractData, - weChatGroup, -} from "@/pages/pc/ckbox/data"; - -import { WechatGroup } from "./api"; -const { login2 } = useUserStore.getState(); -//获取触客宝基础信息 -export const chatInitAPIdata = async () => { - try { - //获取联系人列表 - const contractList = await getAllContactList(); - - //获取联系人列表 - asyncContractList(contractList); - - //获取群列表 - const groupList = await getAllGroupList(); - - await asyncWeChatGroup(groupList); - - // 提取不重复的wechatAccountId组 - const uniqueWechatAccountIds: number[] = getUniqueWechatAccountIds( - contractList, - groupList, - ); - - //获取控制终端列表 - const kfUserList: KfUserListData[] = - await getControlTerminalListByWechatAccountIds(uniqueWechatAccountIds); - - //获取用户列表 - await asyncKfUserList(kfUserList); - - //获取标签列表 - const countLables = await getCountLables(); - await asyncCountLables(countLables); - - //获取消息会话列表并按lastUpdateTime排序 - const filterUserSessions = contractList?.filter( - v => v?.config && v.config?.chat, - ); - const filterGroupSessions = groupList?.filter( - v => v?.config && v.config?.chat, - ); - //排序功能 - const sortedSessions = [...filterUserSessions, ...filterGroupSessions].sort( - (a, b) => { - // 如果lastUpdateTime不存在,则将其排在最后 - if (!a.lastUpdateTime) return 1; - if (!b.lastUpdateTime) return -1; - - // 首先按时间降序排列(最新的在前面) - const timeCompare = - new Date(b.lastUpdateTime).getTime() - - new Date(a.lastUpdateTime).getTime(); - - // 如果时间相同,则按未读消息数量降序排列 - if (timeCompare === 0) { - // 如果unreadCount不存在,则将其排在后面 - const aUnread = a.unreadCount || 0; - const bUnread = b.unreadCount || 0; - return bUnread - aUnread; // 未读消息多的排在前面 - } - - return timeCompare; - }, - ); - //会话数据同步 - asyncChatSessions(sortedSessions); - - return { - contractList, - groupList, - kfUserList, - }; - } catch (error) { - console.error("获取联系人列表失败:", error); - return []; - } -}; -//发起soket连接 -export const initSocket = () => { - // 检查WebSocket是否已经连接 - const { status } = useWebSocketStore.getState(); - - // 如果已经连接或正在连接,则不重复连接 - if (["connected", "connecting"].includes(status)) { - console.log("WebSocket已连接或正在连接,跳过重复连接", { status }); - return; - } - - // 从store获取token和accountId - const { token2 } = useUserStore.getState(); - const { getAccountId } = useCkChatStore.getState(); - const Token = token2; - const accountId = getAccountId(); - // 使用WebSocket store初始化连接 - const { connect } = useWebSocketStore.getState(); - - // 连接WebSocket - connect({ - accessToken: Token, - accountId: Number(accountId), - client: "kefu-client", - cmdType: "CmdSignIn", - seq: +new Date(), - }); -}; - -export const getCountLables = async () => { - const LablesRes = await Promise.all( - [1, 2].map(item => - WechatGroup({ - groupType: item, - }), - ), - ); - const [friend, group] = LablesRes; - const countLables = [ - ...[ - { - id: 0, - groupName: "默认群分组", - groupType: 2, - }, - ], - ...group, - ...friend, - ...[ - { - id: 0, - groupName: "未分组", - groupType: 1, - }, - ], - ]; - - return countLables; -}; -/** - * 根据标签组织联系人 - * @param contractList 联系人列表 - * @param countLables 标签列表 - * @returns 按标签分组的联系人 - */ - -//获取控制终端列表 -export const getControlTerminalListByWechatAccountIds = ( - WechatAccountIds: number[], -) => { - return Promise.all( - WechatAccountIds.map(id => getControlTerminalList({ id: id })), - ); -}; -// 递归获取所有联系人列表 -export const getAllContactList = async () => { - try { - let allContacts = []; - let prevId = 0; - const count = 1000; - let hasMore = true; - - while (hasMore) { - const contractList = await getContactList({ - prevId, - count, - }); - - if ( - !contractList || - !Array.isArray(contractList) || - contractList.length === 0 - ) { - hasMore = false; - break; - } - - allContacts = [...allContacts, ...contractList]; - - // 如果返回的数据少于请求的数量,说明已经没有更多数据了 - if (contractList.length < count) { - hasMore = false; - } else { - // 获取最后一条数据的id作为下一次请求的prevId - const lastContact = contractList[contractList.length - 1]; - prevId = lastContact.id; - } - } - return allContacts; - } catch (error) { - console.error("获取所有联系人列表失败:", error); - return []; - } -}; - -// 提取不重复的wechatAccountId组 -export const getUniqueWechatAccountIds = ( - contacts: ContractData[], - groupList: weChatGroup[], -) => { - if (!contacts || !Array.isArray(contacts) || contacts.length === 0) { - return []; - } - - // 使用Set来存储不重复的wechatAccountId - const uniqueAccountIdsSet = new Set(); - - // 遍历联系人列表,将每个wechatAccountId添加到Set中 - contacts.forEach(contact => { - if (contact && contact.wechatAccountId) { - uniqueAccountIdsSet.add(contact.wechatAccountId); - } - }); - - // 遍历联系人列表,将每个wechatAccountId添加到Set中 - groupList.forEach(group => { - if (group && group.wechatAccountId) { - uniqueAccountIdsSet.add(group.wechatAccountId); - } - }); - - // 将Set转换为数组并返回 - return Array.from(uniqueAccountIdsSet); -}; -// 递归获取所有群列表 -export const getAllGroupList = async () => { - try { - let allContacts = []; - let prevId = 0; - const count = 1000; - let hasMore = true; - - while (hasMore) { - const contractList = await getGroupList({ - prevId, - count, - }); - - if ( - !contractList || - !Array.isArray(contractList) || - contractList.length === 0 - ) { - hasMore = false; - break; - } - - allContacts = [...allContacts, ...contractList]; - - // 如果返回的数据少于请求的数量,说明已经没有更多数据了 - if (contractList.length < count) { - hasMore = false; - } else { - // 获取最后一条数据的id作为下一次请求的prevId - const lastContact = contractList[contractList.length - 1]; - prevId = lastContact.id; - } - } - - return allContacts; - } catch (error) { - console.error("获取所有群列表失败:", error); - return []; - } -}; - -//获取token -const getToken = () => { - return new Promise((resolve, reject) => { - const params = { - grant_type: "password", - password: "kr123456", - username: "kr_xf3", - // username: "karuo", - // password: "zhiqun1984", - }; - loginWithToken(params) - .then(res => { - login2(res.access_token); - resolve(res.access_token); - }) - .catch(err => { - reject(err); - }); - }); -}; diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/api.ts b/Cunkebao/src/pages/pc/ckbox/weChat/api.ts deleted file mode 100644 index af476f26..00000000 --- a/Cunkebao/src/pages/pc/ckbox/weChat/api.ts +++ /dev/null @@ -1,287 +0,0 @@ -import request from "@/api/request2"; -import { - MessageData, - ChatHistoryResponse, - MessageType, - OnlineStatus, - MessageStatus, - FileUploadResponse, - EmojiData, - QuickReply, - ChatSettings, -} from "./data"; - -//读取聊天信息 -//kf.quwanzhi.com:9991/api/WechatFriend/clearUnreadCount -function jsonToQueryString(json) { - const params = new URLSearchParams(); - for (const key in json) { - if (Object.prototype.hasOwnProperty.call(json, key)) { - params.append(key, json[key]); - } - } - return params.toString(); -} -//转移客户 -export function WechatFriendAllot(params: { - wechatFriendId?: number; - wechatChatroomId?: number; - toAccountId: number; - notifyReceiver: boolean; - comment: string; -}) { - return request( - "/api/wechatFriend/allot?" + jsonToQueryString(params), - undefined, - "PUT", - ); -} - -//获取可转移客服列表 -export function getTransferableAgentList() { - return request("/api/account/myDepartmentAccountsForTransfer", {}, "GET"); -} - -// 微信好友列表 -export function WechatFriendRebackAllot(params: { - wechatFriendId?: number; - wechatChatroomId?: number; -}) { - return request( - "/api/wechatFriend/rebackAllot?" + jsonToQueryString(params), - undefined, - "PUT", - ); -} - -// 微信群列表 -export function WechatGroup(params) { - return request("/api/WechatGroup/list", params, "GET"); -} - -//获取聊天记录-1 清除未读 -export function clearUnreadCount(params) { - return request("/api/WechatFriend/clearUnreadCount", params, "PUT"); -} - -//更新配置 -export function updateConfig(params) { - return request("/api/WechatFriend/updateConfig", params, "PUT"); -} -//获取聊天记录-2 获取列表 -export function getChatMessages(params: { - wechatAccountId: number; - wechatFriendId?: number; - wechatChatroomId?: number; - From: number; - To: number; - Count: number; - olderData: boolean; -}) { - return request("/api/FriendMessage/SearchMessage", params, "GET"); -} -export function getChatroomMessages(params: { - wechatAccountId: number; - wechatFriendId?: number; - wechatChatroomId?: number; - From: number; - To: number; - Count: number; - olderData: boolean; -}) { - return request("/api/ChatroomMessage/SearchMessage", params, "GET"); -} - -//获取群列表 -export function getGroupList(params: { prevId: number; count: number }) { - return request( - "/api/wechatChatroom/listExcludeMembersByPage?", - params, - "GET", - ); -} - -//获取群成员 -export function getGroupMembers(params: { id: number }) { - return request( - "/api/WechatChatroom/listMembersByWechatChatroomId", - params, - "GET", - ); -} - -//触客宝登陆 -export function loginWithToken(params: any) { - return request( - "/token", - params, - "POST", - { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }, - 1000, - ); -} - -// 获取触客宝用户信息 -export function getChuKeBaoUserInfo() { - return request("/api/account/self", {}, "GET"); -} - -// 获取联系人列表 -export const getContactList = (params: { prevId: number; count: number }) => { - return request("/api/wechatFriend/list", params, "GET"); -}; - -//获取控制终端列表 -export const getControlTerminalList = params => { - return request("/api/wechataccount", params, "GET"); -}; - -// 获取聊天历史 -export const getChatHistory = ( - chatId: string, - page: number = 1, - pageSize: number = 50, -): Promise => { - return request(`/v1/chats/${chatId}/messages`, { page, pageSize }, "GET"); -}; - -// 发送消息 -export const sendMessage = ( - chatId: string, - content: string, - type: MessageType = MessageType.TEXT, -): Promise => { - return request(`/v1/chats/${chatId}/messages`, { content, type }, "POST"); -}; - -// 发送文件消息 -export const sendFileMessage = ( - chatId: string, - file: File, - type: MessageType, -): Promise => { - const formData = new FormData(); - formData.append("file", file); - formData.append("type", type); - return request(`/v1/chats/${chatId}/messages/file`, formData, "POST"); -}; - -// 标记消息为已读 -export const markMessageAsRead = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/read`, {}, "PUT"); -}; - -// 标记聊天为已读 -export const markChatAsRead = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/read`, {}, "PUT"); -}; - -// 添加群组成员 -export const addGroupMembers = ( - groupId: string, - memberIds: string[], -): Promise => { - return request(`/v1/groups/${groupId}/members`, { memberIds }, "POST"); -}; - -// 移除群组成员 -export const removeGroupMembers = ( - groupId: string, - memberIds: string[], -): Promise => { - return request(`/v1/groups/${groupId}/members`, { memberIds }, "DELETE"); -}; - -// 获取在线状态 -export const getOnlineStatus = (userId: string): Promise => { - return request(`/v1/users/${userId}/status`, {}, "GET"); -}; - -// 获取消息状态 -export const getMessageStatus = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/status`, {}, "GET"); -}; - -// 上传文件 -export const uploadFile = (file: File): Promise => { - const formData = new FormData(); - formData.append("file", file); - return request("/v1/upload", formData, "POST"); -}; - -// 获取表情包列表 -export const getEmojiList = (): Promise => { - return request("/v1/emojis", {}, "GET"); -}; - -// 获取快捷回复列表 -export const getQuickReplies = (): Promise => { - return request("/v1/quick-replies", {}, "GET"); -}; - -// 添加快捷回复 -export const addQuickReply = (data: { - content: string; - category: string; -}): Promise => { - return request("/v1/quick-replies", data, "POST"); -}; - -// 删除快捷回复 -export const deleteQuickReply = (id: string): Promise => { - return request(`/v1/quick-replies/${id}`, {}, "DELETE"); -}; - -// 获取聊天设置 -export const getChatSettings = (): Promise => { - return request("/v1/chat/settings", {}, "GET"); -}; - -// 更新聊天设置 -export const updateChatSettings = ( - settings: Partial, -): Promise => { - return request("/v1/chat/settings", settings, "PUT"); -}; - -// 删除聊天会话 -export const deleteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}`, {}, "DELETE"); -}; - -// 置顶聊天会话 -export const pinChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/pin`, {}, "PUT"); -}; - -// 取消置顶聊天会话 -export const unpinChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/unpin`, {}, "PUT"); -}; - -// 静音聊天会话 -export const muteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/mute`, {}, "PUT"); -}; - -// 取消静音聊天会话 -export const unmuteChatSession = (chatId: string): Promise => { - return request(`/v1/chats/${chatId}/unmute`, {}, "PUT"); -}; - -// 转发消息 -export const forwardMessage = ( - messageId: string, - targetChatIds: string[], -): Promise => { - return request("/v1/messages/forward", { messageId, targetChatIds }, "POST"); -}; - -// 撤回消息 -export const recallMessage = (messageId: string): Promise => { - return request(`/v1/messages/${messageId}/recall`, {}, "PUT"); -}; diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/ChatWindow.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/ChatWindow.module.scss deleted file mode 100644 index ceeff66e..00000000 --- a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/ChatWindow.module.scss +++ /dev/null @@ -1,404 +0,0 @@ -.chatWindow { - height: 100%; - display: flex; - flex-direction: row; -} - -.chatMain { - flex: 1; - display: flex; - flex-direction: column; - min-width: 0; -} - -.chatHeader { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 16px; - background: #fff; - border-bottom: 1px solid #f0f0f0; - height: 64px; - min-height: 64px; - flex-shrink: 0; - gap: 16px; // 确保信息区域和按钮区域有足够间距 - - .chatHeaderInfo { - display: flex; - align-items: center; - gap: 12px; - flex: 1; - min-width: 0; // 防止flex子元素溢出 - - :global(.ant-avatar) { - flex-shrink: 0; - width: 40px; - height: 40px; - } - - .chatHeaderDetails { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - min-width: 0; - - .chatHeaderName { - font-size: 16px; - font-weight: 600; - color: #262626; - display: flex; - align-items: center; - gap: 8px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-right: 30px; - - .chatHeaderOnlineStatus { - font-size: 12px; - color: #52c41a; - font-weight: normal; - flex-shrink: 0; // 防止在线状态被压缩 - } - } - - .chatHeaderType { - font-size: 12px; - color: #8c8c8c; - margin-top: 2px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .chatHeaderSubInfo { - display: flex; - gap: 12px; - margin-top: 2px; - font-size: 12px; - overflow: hidden; - - .chatHeaderRemark { - color: #1890ff; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .chatHeaderWechatId { - color: #8c8c8c; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - } - } - - .headerButton { - color: #8c8c8c; - border: none; - padding: 8px; - - &:hover { - color: #1890ff; - background: #f5f5f5; - } - } -} - -.chatContent { - flex: 1; - overflow: visible; - background: #f5f5f5; - display: flex; - flex-direction: column; - - .messagesContainer { - height: 100%; - overflow-y: auto; - padding: 16px; - - .loadingContainer { - display: flex; - justify-content: center; - align-items: center; - height: 100px; - color: #8c8c8c; - } - } -} - -// 右侧个人资料卡片 -.profileSider { - background: #fff; - border-left: 1px solid #f0f0f0; - display: flex; - flex-direction: column; - height: 100%; - flex-shrink: 0; - - .profileSiderContent { - display: flex; - flex-direction: column; - height: 100%; - - .profileHeader { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px; - border-bottom: 1px solid #f0f0f0; - background: #fafafa; - flex-shrink: 0; - - h3 { - margin: 0; - font-size: 16px; - font-weight: 600; - color: #262626; - } - - .closeButton { - color: #8c8c8c; - border: none; - padding: 4px; - - &:hover { - color: #1890ff; - background: #f5f5f5; - } - } - } - - .profileContent { - flex: 1; - overflow-y: auto; - padding: 16px; - height: 0; // 确保flex子元素能够正确计算高度 - - .profileCard { - margin-bottom: 16px; - border-radius: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - - &:last-child { - margin-bottom: 0; - } - - :global(.ant-card-head) { - border-bottom: 1px solid #f0f0f0; - padding: 0 16px; - - :global(.ant-card-head-title) { - font-size: 14px; - font-weight: 600; - color: #262626; - } - } - - :global(.ant-card-body) { - padding: 16px; - } - } - - .profileBasic { - display: flex; - align-items: center; - gap: 16px; - - :global(.ant-avatar) { - flex-shrink: 0; - width: 48px; - height: 48px; - } - - .profileInfo { - flex: 1; - min-width: 0; - overflow: hidden; - - h4 { - margin: 0 0 8px 0; - font-size: 18px; - font-weight: 600; - color: #262626; - } - - .profileNickname { - margin: 0 0 8px 0; - font-size: 18px; - font-weight: 600; - color: #262626; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - cursor: pointer; - } - - .profileStatus { - margin: 0 0 4px 0; - font-size: 12px; - color: #52c41a; - } - - .profilePosition { - margin: 0; - font-size: 12px; - color: #8c8c8c; - } - - .profileRemark { - margin: 0 0 4px 0; - font-size: 12px; - color: #1890ff; - - :global(.ant-input) { - font-size: 12px; - } - - :global(.ant-btn) { - padding: 0; - width: 20px; - height: 20px; - display: flex; - align-items: center; - justify-content: center; - } - } - - .profileWechatId { - margin: 0; - font-size: 12px; - color: #8c8c8c; - } - } - } - - .contractInfo { - .contractItem { - align-items: center; - margin-bottom: 12px; - font-size: 14px; - color: #262626; - - &:last-child { - margin-bottom: 0; - } - - :global(.anticon) { - color: #8c8c8c; - font-size: 16px; - width: 16px; - } - - .contractItemText { - padding-left: 10px; - } - } - } - - .tagsContainer { - display: flex; - flex-wrap: wrap; - gap: 6px; - - :global(.ant-tag) { - margin: 0; - border-radius: 12px; - font-size: 12px; - } - } - - .bioText { - margin: 0; - font-size: 14px; - line-height: 1.6; - color: #595959; - } - - .profileActions { - margin-top: 24px; - } - } - } -} - -.messageTime { - text-align: center; - padding: 4px 0; - font-size: 12px; - color: #999; - margin: 20px 0; -} - -// 响应式设计 -@media (max-width: 1200px) { - .profileSider { - width: 260px !important; - } -} - -@media (max-width: 768px) { - .chatWindow { - flex-direction: column; - } - - .profileSider { - width: 100% !important; - height: 300px; - border-left: none; - border-top: 1px solid #f0f0f0; - } - - .chatHeader { - padding: 0 12px; - height: 56px; - min-height: 56px; - - .chatHeaderInfo { - .chatHeaderDetails { - .chatHeaderName { - font-size: 14px; - } - } - } - } - - .chatContent { - .messagesContainer { - padding: 12px; - } - } - - .profileContent { - padding: 12px; - - .profileCard { - margin-bottom: 12px; - - :global(.ant-card-body) { - padding: 12px; - } - } - - .profileBasic { - gap: 12px; - - .profileInfo { - h4 { - font-size: 16px; - } - } - } - - .contractInfo { - .contractItem { - font-size: 13px; - margin-bottom: 10px; - } - } - } -} diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss deleted file mode 100644 index 36977bd1..00000000 --- a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss +++ /dev/null @@ -1,181 +0,0 @@ -// MessageEnter 组件样式 - 微信风格 -.chatFooter { - background: #f7f7f7; - border-top: 1px solid #e1e1e1; - padding: 0; - height: auto; - min-height: 100px; -} - -.inputContainer { - padding: 8px 12px; - display: flex; - flex-direction: column; - gap: 6px; -} - -.inputToolbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 4px 0; - border-bottom: none; -} - -.leftTool { - display: flex; - gap: 2px; - align-items: center; -} - -.toolbarButton { - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - color: #666; - font-size: 16px; - transition: all 0.15s; - border: none; - background: transparent; - - &:hover { - background: #e6e6e6; - color: #333; - } - - &:active { - background: #d9d9d9; - } -} - -.rightTool { - display: flex; - gap: 12px; - align-items: center; -} - -.rightToolItem { - display: flex; - align-items: center; - gap: 3px; - color: #666; - font-size: 11px; - cursor: pointer; - padding: 3px 6px; - border-radius: 3px; - transition: all 0.15s; - - &:hover { - background: #e6e6e6; - color: #333; - } -} - -.inputArea { - display: flex; - flex-direction: column; - padding: 4px 0; -} - -.inputWrapper { - border: 1px solid #d1d1d1; - border-radius: 4px; - background: #fff; - overflow: hidden; - - &:focus-within { - border-color: #07c160; - } -} - -.messageInput { - width: 100%; - border: none; - resize: none; - font-size: 13px; - line-height: 1.4; - padding: 8px 10px; - background: transparent; - - &:focus { - box-shadow: none; - outline: none; - } - - &::placeholder { - color: #b3b3b3; - } -} - -.sendButtonArea { - padding: 8px 10px; - display: flex; - justify-content: flex-end; -} - -.sendButton { - height: 32px; - border-radius: 4px; - font-weight: normal; - min-width: 60px; - font-size: 13px; - background: #07c160; - border-color: #07c160; - - &:hover { - background: #06ad56; - border-color: #06ad56; - } - - &:active { - background: #059748; - border-color: #059748; - } - - &:disabled { - background: #b3b3b3; - border-color: #b3b3b3; - opacity: 1; - } -} - -.inputHint { - font-size: 11px; - color: #999; - text-align: right; - margin-top: 2px; -} - -// 响应式设计 -@media (max-width: 768px) { - .inputContainer { - padding: 8px 12px; - } - - .inputToolbar { - flex-wrap: wrap; - gap: 8px; - } - - .rightTool { - gap: 8px; - } - - .rightToolItem { - font-size: 11px; - padding: 2px 6px; - } - - .inputArea { - flex-direction: column; - gap: 8px; - } - - .sendButton { - align-self: flex-end; - min-width: 60px; - } -} diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/chatRecord/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/chatRecord/index.tsx deleted file mode 100644 index 3068e42d..00000000 --- a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/chatRecord/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useState } from "react"; -import { Button, Modal, Input, DatePicker, message } from "antd"; -import { MessageOutlined } from "@ant-design/icons"; -import dayjs from "dayjs"; -import { useWeChatStore } from "@/store/module/weChat/weChat"; - -const { RangePicker } = DatePicker; - -interface ChatRecordProps { - className?: string; - disabled?: boolean; -} - -const ChatRecord: React.FC = ({ - className, - disabled = false, -}) => { - const [visible, setVisible] = useState(false); - const [searchContent, setSearchContent] = useState(""); - const [dateRange, setDateRange] = useState<[dayjs.Dayjs, dayjs.Dayjs] | null>( - null, - ); - const [loading, setLoading] = useState(false); - const SearchMessage = useWeChatStore(state => state.SearchMessage); - - // 打开弹窗 - const openModal = () => { - setVisible(true); - }; - - // 关闭弹窗并重置状态 - const closeModal = () => { - setVisible(false); - setSearchContent(""); - setDateRange(null); - setLoading(false); - }; - - // 执行查找 - const handleSearch = async () => { - if (!dateRange) { - message.warning("请选择时间范围"); - return; - } - - try { - setLoading(true); - const [From, To] = dateRange; - const searchData = { - From: From.unix() * 1000, - To: To.unix() * 1000, - keyword: searchContent.trim(), - }; - await SearchMessage(searchData); - - message.success("查找完成"); - closeModal(); - } catch (error) { - console.error("查找失败:", error); - message.error("查找失败,请重试"); - } finally { - setLoading(false); - } - }; - - return ( - <> -
- - 聊天记录 -
- - - - - , - ]} - > -
- {/* 时间范围选择 */} -
-
- 时间范围 -
- -
- - {/* 查找内容输入 */} -
-
- 查找内容 -
- setSearchContent(e.target.value)} - size="large" - maxLength={100} - showCount - disabled={loading} - /> -
-
-
- - ); -}; - -export default ChatRecord; diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx deleted file mode 100644 index be2f9cb2..00000000 --- a/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import React, { useState } from "react"; -import { Button, Modal, Select, Input, message } from "antd"; -import { ShareAltOutlined } from "@ant-design/icons"; -import { - getTransferableAgentList, - WechatFriendAllot, - WechatFriendRebackAllot, -} from "@/pages/pc/ckbox/weChat/api"; -import { useCurrentContact } from "@/store/module/weChat/weChat"; -import { useCkChatStore } from "@/store/module/ckchat/ckchat"; -import { contractService, weChatGroupService } from "@/utils/db"; -const { TextArea } = Input; -const { Option } = Select; - -interface ToContractProps { - className?: string; - disabled?: boolean; -} -interface DepartItem { - id: number; - userName: string; - realName: string; - nickname: string; - avatar: string; - memo: string; - departmentId: number; - alive: boolean; -} - -const ToContract: React.FC = ({ - className, - disabled = false, -}) => { - const currentContact = useCurrentContact(); - const [visible, setVisible] = useState(false); - const [selectedTarget, setSelectedTarget] = useState(null); - const [comment, setComment] = useState(""); - const [loading, setLoading] = useState(false); - const [customerServiceList, setCustomerServiceList] = useState( - [], - ); - const deleteChatSession = useCkChatStore(state => state.deleteChatSession); - // 打开弹窗 - const openModal = () => { - setVisible(true); - getTransferableAgentList().then(data => { - setCustomerServiceList(data); - }); - }; - - // 关闭弹窗并重置状态 - const closeModal = () => { - setVisible(false); - setSelectedTarget(null); - setComment(""); - setLoading(false); - }; - - // 确定转给他人 - const handleConfirm = async () => { - if (!selectedTarget) { - message.warning("请选择目标客服"); - return; - } - - try { - setLoading(true); - - console.log(currentContact); - - // 调用转接接口 - if (currentContact) { - if ("chatroomId" in currentContact && currentContact.chatroomId) { - await WechatFriendAllot({ - wechatChatroomId: currentContact.id, - toAccountId: selectedTarget as number, - notifyReceiver: true, - comment: comment.trim(), - }); - } else { - await WechatFriendAllot({ - wechatFriendId: currentContact.id, - toAccountId: selectedTarget as number, - notifyReceiver: true, - comment: comment.trim(), - }); - } - } - - message.success("转接成功"); - try { - // 删除聊天会话 - deleteChatSession(currentContact.id); - // 删除本地数据库记录 - if ("chatroomId" in currentContact) { - await weChatGroupService.delete(currentContact.id); - } else { - await contractService.delete(currentContact.id); - } - } catch (deleteError) { - console.error("删除本地数据失败:", deleteError); - } - closeModal(); - } catch (error) { - console.error("转接失败:", error); - message.error("转接失败,请重试"); - } finally { - setLoading(false); - } - }; - - // 一键转回 - const handleReturn = async () => { - try { - setLoading(true); - - // 调用转回接口 - if (currentContact) { - if ("chatroomId" in currentContact && currentContact.chatroomId) { - await WechatFriendRebackAllot({ - wechatChatroomId: currentContact.id, - }); - } else { - await WechatFriendRebackAllot({ - wechatFriendId: currentContact.id, - }); - } - } - - message.success("转回成功"); - try { - // 删除聊天会话 - deleteChatSession(currentContact.id); - // 删除本地数据库记录 - if ("chatroomId" in currentContact) { - await weChatGroupService.delete(currentContact.id); - } else { - await contractService.delete(currentContact.id); - } - } catch (deleteError) { - console.error("删除本地数据失败:", deleteError); - } - closeModal(); - } catch (error) { - console.error("转回失败:", error); - message.error("转回失败,请重试"); - } finally { - setLoading(false); - } - }; - - return ( - <> -
- - 转给他人 -
- - - -
- - -
- , - ]} - > -
- {/* 目标客服选择 */} -
-
- 目标客服 -
- -
- - {/* 附言输入 */} -
-
- 附言 -
-