From 02b394f31b50c42910309a5dcbe62bdba2b32a35 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: Fri, 29 Aug 2025 15:55:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=81=8A=E5=A4=A9=E7=AA=97=E5=8F=A3):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=B3=E4=BE=A7=E8=81=94=E7=B3=BB=E4=BA=BA?= =?UTF-8?q?=E8=B5=84=E6=96=99=E5=8D=A1=E7=89=87=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将联系人资料卡片从主聊天窗口组件中拆分出来,创建独立的Person组件 实现资料卡片的样式和功能,包括基本信息展示、备注编辑和操作按钮 添加响应式设计支持移动端显示 --- .../components/Person/Person.module.scss | 204 +++++++++++++ .../ChatWindow/components/Person/index.tsx | 272 ++++++++++++++++++ .../pc/ckbox/components/ChatWindow/index.tsx | 228 +-------------- 3 files changed, 487 insertions(+), 217 deletions(-) create mode 100644 Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/Person.module.scss create mode 100644 Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/Person.module.scss b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/Person.module.scss new file mode 100644 index 00000000..7d96cc69 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/Person.module.scss @@ -0,0 +1,204 @@ +.profileSider { + background: #fff; + border-left: 1px solid #e8e8e8; + height: 100%; + overflow-y: auto; + + .profileContainer { + padding: 16px; + height: 100%; + display: flex; + flex-direction: column; + } + + .profileHeader { + display: flex; + justify-content: flex-end; + margin-bottom: 16px; + + .closeButton { + color: #8c8c8c; + + &:hover { + color: #262626; + background: #f5f5f5; + } + } + } + + .profileBasic { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 24px; + padding-bottom: 24px; + border-bottom: 1px solid #f0f0f0; + + .profileInfo { + margin-top: 16px; + text-align: center; + width: 100%; + + .profileNickname { + margin: 0 0 8px 0; + font-size: 18px; + font-weight: 600; + color: #262626; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .profileRemark { + margin-bottom: 12px; + + .remarkText { + color: #8c8c8c; + font-size: 14px; + cursor: pointer; + + &:hover { + color: #1890ff; + } + } + } + + .profileStatus { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + color: #52c41a; + font-size: 14px; + + .statusDot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #52c41a; + } + } + } + } + + .profileCard { + margin-bottom: 16px; + border-radius: 8px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03); + + :global(.ant-card-head) { + padding: 0 16px; + min-height: 40px; + border-bottom: 1px solid #f0f0f0; + + :global(.ant-card-head-title) { + font-size: 14px; + font-weight: 500; + color: #262626; + } + } + + :global(.ant-card-body) { + padding: 16px; + } + + .infoItem { + display: flex; + align-items: center; + margin-bottom: 12px; + + &:last-child { + margin-bottom: 0; + } + + .infoIcon { + color: #8c8c8c; + margin-right: 8px; + width: 16px; + flex-shrink: 0; + } + + .infoLabel { + color: #8c8c8c; + font-size: 14px; + width: 60px; + flex-shrink: 0; + } + + .infoValue { + color: #262626; + font-size: 14px; + flex: 1; + word-break: break-all; + } + } + + .tagsContainer { + display: flex; + flex-wrap: wrap; + gap: 8px; + + :global(.ant-tag) { + margin: 0; + border-radius: 4px; + } + } + + .bioText { + margin: 0; + color: #595959; + font-size: 14px; + line-height: 1.6; + word-break: break-word; + } + } + + .profileActions { + margin-top: auto; + padding-top: 16px; + + :global(.ant-btn) { + border-radius: 6px; + font-weight: 500; + } + } +} + +// 响应式设计 +@media (max-width: 768px) { + .profileSider { + width: 280px !important; + + .profileContainer { + padding: 12px; + } + + .profileBasic { + .profileInfo { + .profileNickname { + font-size: 16px; + } + } + } + + .profileCard { + :global(.ant-card-body) { + padding: 12px; + } + + .infoItem { + margin-bottom: 10px; + + .infoLabel { + width: 50px; + font-size: 13px; + } + + .infoValue { + font-size: 13px; + } + } + } + } +} \ No newline at end of file diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx new file mode 100644 index 00000000..da0dea8b --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx @@ -0,0 +1,272 @@ +import React, { useState, useEffect } from "react"; +import { + Layout, + Input, + Button, + Avatar, + Tooltip, + Card, + Tag, + message, +} from "antd"; +import { + PhoneOutlined, + VideoCameraOutlined, + UserOutlined, + TeamOutlined, + MailOutlined, + EnvironmentOutlined, + CalendarOutlined, + BankOutlined, + CloseOutlined, + StarOutlined, + EditOutlined, + CheckOutlined, +} from "@ant-design/icons"; +import { ContractData } from "@/pages/pc/ckbox/data"; +import { useCkChatStore } from "@/store/module/ckchat"; +import styles from "./Person.module.scss"; + +const { Sider } = Layout; + +interface PersonProps { + contract: ContractData; + showProfile: boolean; + onToggleProfile?: () => void; +} + +const Person: React.FC = ({ + contract, + showProfile, + onToggleProfile, +}) => { + const [messageApi, contextHolder] = message.useMessage(); + const [isEditingRemark, setIsEditingRemark] = useState(false); + const [remarkValue, setRemarkValue] = useState(contract.conRemark || ""); + + const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser()); + + // 当contract变化时更新备注值 + useEffect(() => { + setRemarkValue(contract.conRemark || ""); + setIsEditingRemark(false); + }, [contract.conRemark]); + + // 处理备注保存 + const handleSaveRemark = () => { + // 这里应该调用API保存备注到后端 + // 暂时只更新本地状态 + messageApi.success("备注保存成功"); + setIsEditingRemark(false); + // 更新contract对象中的备注(实际项目中应该通过props回调或状态管理) + }; + + // 处理取消编辑 + const handleCancelEdit = () => { + setRemarkValue(contract.conRemark || ""); + setIsEditingRemark(false); + }; + + // 模拟联系人详细信息 + const contractInfo = { + name: contract.name, + nickname: contract.nickname, + conRemark: remarkValue, // 使用当前编辑的备注值 + alias: contract.alias, + wechatId: contract.wechatId, + avatar: contract.avatar, + phone: contract.phone || "-", + email: contract.email || "-", + department: contract.department || "-", + position: contract.position || "-", + company: contract.company || "-", + location: contract.location || "-", + joinDate: contract.joinDate || "-", + status: "在线", + tags: contract.labels, + bio: contract.bio || "-", + }; + + if (!showProfile) { + return null; + } + + return ( + <> + {contextHolder} + +
+ {/* 关闭按钮 */} +
+
+ + {/* 头像和基本信息 */} +
+ } + /> +
+ +

+ {contractInfo.nickname || contractInfo.name} +

+
+ +
+ {JSON.stringify(kfSelectedUser)} + {isEditingRemark ? ( +
+ setRemarkValue(e.target.value)} + placeholder="请输入备注" + size="small" + style={{ flex: 1 }} + /> +
+ ) : ( +
+ + {contractInfo.conRemark || "点击添加备注"} + +
+ )} +
+ +
+ + {contractInfo.status} +
+
+
+ + {/* 详细信息卡片 */} + +
+ + 微信号: + {contractInfo.wechatId} +
+
+ + 昵称: + {contractInfo.alias} +
+
+ + 电话: + {contractInfo.phone} +
+
+ + 邮箱: + {contractInfo.email} +
+
+ + 部门: + + {contractInfo.department} + +
+
+ + 职位: + {contractInfo.position} +
+
+ + 公司: + {contractInfo.company} +
+
+ + 地区: + {contractInfo.location} +
+
+ + 入职时间: + {contractInfo.joinDate} +
+
+ + {/* 标签 */} + +
+ {contractInfo.tags?.map((tag, index) => ( + + {tag} + + ))} +
+
+ + {/* 个人简介 */} + +

{contractInfo.bio}

+
+ + {/* 操作按钮 */} +
+ + +
+
+
+ + ); +}; + +export default Person; diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx index a1e94395..037edf5f 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx @@ -9,9 +9,6 @@ import { Menu, message, Tooltip, - Badge, - Card, - Tag, Modal, } from "antd"; import { @@ -23,14 +20,6 @@ import { VideoCameraOutlined, MoreOutlined, UserOutlined, - TeamOutlined, - MailOutlined, - EnvironmentOutlined, - CalendarOutlined, - BankOutlined, - CloseOutlined, - StarOutlined, - EnvironmentOutlined as LocationOutlined, AudioOutlined, AudioOutlined as AudioHoldOutlined, DownloadOutlined, @@ -42,8 +31,9 @@ import { FileExcelOutlined, FilePptOutlined, PlayCircleFilled, - EditOutlined, - CheckOutlined, + EnvironmentOutlined, + TeamOutlined, + StarOutlined, } from "@ant-design/icons"; import { ChatRecord, ContractData } from "@/pages/pc/ckbox/data"; import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api"; @@ -51,7 +41,8 @@ import styles from "./ChatWindow.module.scss"; import { useWebSocketStore, WebSocketMessage } from "@/store/module/websocket"; import { formatWechatTime } from "@/utils/common"; import { useCkChatStore } from "@/store/module/ckchat"; -const { Header, Content, Footer, Sider } = Layout; +import Person from "./components/Person"; +const { Header, Content, Footer } = Layout; const { TextArea } = Input; interface ChatWindowProps { @@ -75,8 +66,6 @@ const ChatWindow: React.FC = ({ const [pendingVideoRequests, setPendingVideoRequests] = useState< Record >({}); - const [isEditingRemark, setIsEditingRemark] = useState(false); - const [remarkValue, setRemarkValue] = useState(contract.conRemark || ""); const messagesEndRef = useRef(null); const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser()); @@ -100,12 +89,6 @@ const ChatWindow: React.FC = ({ }); }, [contract.id]); - // 当contract变化时更新备注值 - useEffect(() => { - setRemarkValue(contract.conRemark || ""); - setIsEditingRemark(false); - }, [contract.conRemark]); - useEffect(() => { // 只有在非视频加载操作时才自动滚动到底部 // 检查是否有视频正在加载中 @@ -792,21 +775,6 @@ const ChatWindow: React.FC = ({ ); }; - // 处理备注保存 - const handleSaveRemark = () => { - // 这里应该调用API保存备注到后端 - // 暂时只更新本地状态 - messageApi.success("备注保存成功"); - setIsEditingRemark(false); - // 更新contract对象中的备注(实际项目中应该通过props回调或状态管理) - }; - - // 处理取消编辑 - const handleCancelEdit = () => { - setRemarkValue(contract.conRemark || ""); - setIsEditingRemark(false); - }; - const chatMenu = ( }> @@ -828,26 +796,6 @@ const ChatWindow: React.FC = ({ ); - // 模拟联系人详细信息 - const contractInfo = { - name: contract.name, - nickname: contract.nickname, - conRemark: remarkValue, // 使用当前编辑的备注值 - alias: contract.alias, - wechatId: contract.wechatId, - avatar: contract.avatar, - phone: contract.phone || "-", - email: contract.email || "-", - department: contract.department || "-", - position: contract.position || "-", - company: contract.company || "-", - location: contract.location || "-", - joinDate: contract.joinDate || "-", - status: "在线", - tags: contract.labels, - bio: contract.bio || "-", - }; - return ( {contextHolder} @@ -944,7 +892,7 @@ const ChatWindow: React.FC = ({ - - - - - - )} + {/* 素材选择模态框 */}