群成员功能
This commit is contained in:
51
Cunkebao/src/components/MemberSelection/index.tsx
Normal file
51
Cunkebao/src/components/MemberSelection/index.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Modal, Checkbox, Avatar, List, Button } from 'antd';
|
||||
|
||||
interface MemberSelectionProps {
|
||||
visible: boolean;
|
||||
members: { id: string; nickname: string; avatar: string }[];
|
||||
onCancel: () => void;
|
||||
onConfirm: (selectedIds: string[]) => void;
|
||||
}
|
||||
|
||||
const MemberSelection: React.FC<MemberSelectionProps> = ({ visible, members, onCancel, onConfirm }) => {
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||
|
||||
const handleToggle = (id: string) => {
|
||||
const newSelectedIds = selectedIds.includes(id)
|
||||
? selectedIds.filter(memberId => memberId !== id)
|
||||
: [...selectedIds, id];
|
||||
setSelectedIds(newSelectedIds);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(selectedIds);
|
||||
setSelectedIds([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="选择要删除的成员"
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={handleConfirm}
|
||||
okText="删除"
|
||||
cancelText="取消"
|
||||
>
|
||||
<List
|
||||
dataSource={members}
|
||||
renderItem={member => (
|
||||
<List.Item key={member.id} onClick={() => handleToggle(member.id)} style={{ cursor: 'pointer' }}>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar src={member.avatar} />}
|
||||
title={member.nickname}
|
||||
/>
|
||||
<Checkbox checked={selectedIds.includes(member.id)} />
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemberSelection;
|
||||
@@ -0,0 +1,708 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Layout,
|
||||
Input,
|
||||
Button,
|
||||
Avatar,
|
||||
Tooltip,
|
||||
Card,
|
||||
Tag,
|
||||
message,
|
||||
Modal,
|
||||
} from "antd";
|
||||
import {
|
||||
PhoneOutlined,
|
||||
VideoCameraOutlined,
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
MailOutlined,
|
||||
EnvironmentOutlined,
|
||||
CalendarOutlined,
|
||||
BankOutlined,
|
||||
CloseOutlined,
|
||||
StarOutlined,
|
||||
EditOutlined,
|
||||
CheckOutlined,
|
||||
PlusOutlined,
|
||||
NotificationOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
||||
import { useWebSocketStore } from "@/store/module/websocket/websocket";
|
||||
import styles from "./Person.module.scss";
|
||||
|
||||
const { Sider } = Layout;
|
||||
|
||||
interface PersonProps {
|
||||
contract: ContractData | weChatGroup;
|
||||
showProfile: boolean;
|
||||
onToggleProfile?: () => void;
|
||||
}
|
||||
|
||||
const Person: React.FC<PersonProps> = ({
|
||||
contract,
|
||||
showProfile,
|
||||
onToggleProfile,
|
||||
}) => {
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [isEditingRemark, setIsEditingRemark] = useState(false);
|
||||
const [remarkValue, setRemarkValue] = useState(contract.conRemark || "");
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>(
|
||||
contract.labels || [],
|
||||
);
|
||||
const [allAvailableTags, setAllAvailableTags] = useState<string[]>([]);
|
||||
const [isAddingTag, setIsAddingTag] = useState(false);
|
||||
const [newTagValue, setNewTagValue] = useState("");
|
||||
|
||||
// 判断是否为群聊
|
||||
const isGroup = 'chatroomId' in contract;
|
||||
|
||||
// 群聊相关状态
|
||||
const [isEditingGroupName, setIsEditingGroupName] = useState(false);
|
||||
const [groupNameValue, setGroupNameValue] = useState(contract.name || '');
|
||||
const [isEditingGroupNotice, setIsEditingGroupNotice] = useState(false);
|
||||
const [groupNoticeValue, setGroupNoticeValue] = useState(contract.notice || '');
|
||||
const [isEditingSelfDisplayName, setIsEditingSelfDisplayName] = useState(false);
|
||||
const [selfDisplayNameValue, setSelfDisplayNameValue] = useState(contract.selfDisplyName || '');
|
||||
const [isGroupNoticeModalVisible, setIsGroupNoticeModalVisible] = useState(false);
|
||||
|
||||
// 构建联系人或群聊详细信息
|
||||
|
||||
const kfSelectedUser = useCkChatStore(state =>
|
||||
state.getKfUserInfo(contract.wechatAccountId || 0),
|
||||
);
|
||||
const { sendCommand } = useWebSocketStore();
|
||||
|
||||
// 获取所有可用标签
|
||||
useEffect(() => {
|
||||
const fetchAvailableTags = async () => {
|
||||
try {
|
||||
// 从kfSelectedUser.labels和contract.labels合并获取所有标签
|
||||
const kfTags = kfSelectedUser?.labels || [];
|
||||
const contractTags = contract.labels || [];
|
||||
const allTags = [...new Set([...kfTags, ...contractTags])];
|
||||
setAllAvailableTags(allTags);
|
||||
} catch (error) {
|
||||
console.error("获取标签失败:", error);
|
||||
}
|
||||
};
|
||||
fetchAvailableTags();
|
||||
}, [kfSelectedUser, contract.labels]);
|
||||
|
||||
// 当contract变化时更新各种值
|
||||
useEffect(() => {
|
||||
setRemarkValue(contract.conRemark || "");
|
||||
setIsEditingRemark(false);
|
||||
setSelectedTags(contract.labels || []);
|
||||
|
||||
if (isGroup) {
|
||||
setGroupNameValue(contract.name || '');
|
||||
setIsEditingGroupName(false);
|
||||
setGroupNoticeValue(contract.notice || '');
|
||||
setIsEditingGroupNotice(false);
|
||||
setSelfDisplayNameValue(contract.selfDisplyName || '');
|
||||
setIsEditingSelfDisplayName(false);
|
||||
}
|
||||
}, [contract.conRemark, contract.labels, contract.name, contract.notice, contract.selfDisplyName, isGroup]);
|
||||
|
||||
// 处理备注保存
|
||||
const handleSaveRemark = () => {
|
||||
if (isGroup) {
|
||||
// 群聊备注修改
|
||||
sendCommand("CmdModifyGroupRemark", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
chatroomId: contract.chatroomId,
|
||||
newRemark: remarkValue,
|
||||
});
|
||||
} else {
|
||||
// 好友备注修改
|
||||
sendCommand("CmdModifyFriendRemark", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatFriendId: contract.id,
|
||||
newRemark: remarkValue,
|
||||
});
|
||||
}
|
||||
|
||||
messageApi.success("备注保存成功");
|
||||
setIsEditingRemark(false);
|
||||
// 更新contract对象中的备注(实际项目中应该通过props回调或状态管理)
|
||||
};
|
||||
|
||||
// 处理群名称保存
|
||||
const handleSaveGroupName = () => {
|
||||
sendCommand("CmdChatroomOperate", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
chatroomOperateType: 6,
|
||||
extra: `{\"chatroomName\":\"${groupNameValue}\"}`,
|
||||
});
|
||||
|
||||
messageApi.success("群名称修改成功");
|
||||
setIsEditingGroupName(false);
|
||||
};
|
||||
|
||||
// 点击编辑群名称按钮
|
||||
const handleEditGroupName = () => {
|
||||
setGroupNameValue(contract.name || '');
|
||||
setIsEditingGroupName(true);
|
||||
};
|
||||
|
||||
// 处理群公告保存
|
||||
const handleSaveGroupNotice = () => {
|
||||
sendCommand("CmdChatroomOperate", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
chatroomOperateType: 5,
|
||||
extra: `{\"announce\":\"${groupNoticeValue}\"}`,
|
||||
});
|
||||
|
||||
messageApi.success("群公告修改成功");
|
||||
setIsEditingGroupNotice(false);
|
||||
};
|
||||
|
||||
// 点击编辑群公告按钮
|
||||
const handleEditGroupNotice = () => {
|
||||
setGroupNoticeValue(contract.notice || '');
|
||||
setIsGroupNoticeModalVisible(true);
|
||||
};
|
||||
|
||||
// 处理我在本群中的昵称保存
|
||||
const handleSaveSelfDisplayName = () => {
|
||||
sendCommand("CmdChatroomOperate", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
chatroomOperateType: 8,
|
||||
extra: `${selfDisplayNameValue}`,
|
||||
});
|
||||
|
||||
messageApi.success("群昵称修改成功");
|
||||
setIsEditingSelfDisplayName(false);
|
||||
};
|
||||
|
||||
// 点击编辑群昵称按钮
|
||||
const handleEditSelfDisplayName = () => {
|
||||
setSelfDisplayNameValue(contract.selfDisplyName || '');
|
||||
setIsEditingSelfDisplayName(true);
|
||||
};
|
||||
|
||||
// 处理取消编辑
|
||||
const handleCancelEdit = () => {
|
||||
setRemarkValue(contract.conRemark || "");
|
||||
setIsEditingRemark(false);
|
||||
};
|
||||
|
||||
// 处理标签点击切换
|
||||
const handleTagToggle = (tagName: string) => {
|
||||
const newSelectedTags = selectedTags.includes(tagName)
|
||||
? selectedTags.filter(tag => tag !== tagName)
|
||||
: [...selectedTags, tagName];
|
||||
|
||||
setSelectedTags(newSelectedTags);
|
||||
|
||||
// 使用WebSocket发送修改标签命令
|
||||
if (isGroup) {
|
||||
// 群聊标签修改
|
||||
sendCommand("CmdModifyGroupLabel", {
|
||||
labels: newSelectedTags,
|
||||
seq: +new Date(),
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
chatroomId: contract.chatroomId,
|
||||
});
|
||||
} else {
|
||||
// 好友标签修改
|
||||
sendCommand("CmdModifyFriendLabel", {
|
||||
labels: newSelectedTags,
|
||||
seq: +new Date(),
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatFriendId: contract.id,
|
||||
});
|
||||
}
|
||||
|
||||
messageApi.success(
|
||||
`标签"${tagName}"${selectedTags.includes(tagName) ? "已取消" : "已选中"}`,
|
||||
);
|
||||
};
|
||||
|
||||
// 处理新增标签
|
||||
const handleAddTag = () => {
|
||||
if (!newTagValue.trim()) {
|
||||
messageApi.error("请输入标签名称");
|
||||
return;
|
||||
}
|
||||
|
||||
if (allAvailableTags.includes(newTagValue.trim())) {
|
||||
messageApi.error("标签已存在");
|
||||
return;
|
||||
}
|
||||
|
||||
const newTag = newTagValue.trim();
|
||||
|
||||
// 添加到可用标签列表
|
||||
setAllAvailableTags(prev => [...prev, newTag]);
|
||||
|
||||
// 自动选中新添加的标签
|
||||
setSelectedTags(prev => [...prev, newTag]);
|
||||
|
||||
// 使用WebSocket发送新增标签命令
|
||||
if (isGroup) {
|
||||
// 群聊标签修改
|
||||
sendCommand("CmdModifyGroupLabel", {
|
||||
labels: [...selectedTags, newTag],
|
||||
seq: +new Date(),
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
chatroomId: contract.chatroomId,
|
||||
});
|
||||
} else {
|
||||
// 好友标签修改
|
||||
sendCommand("CmdModifyFriendLabel", {
|
||||
labels: [...selectedTags, newTag],
|
||||
seq: +new Date(),
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatFriendId: contract.id || contract.wechatId,
|
||||
});
|
||||
}
|
||||
|
||||
messageApi.success(`标签"${newTag}"添加成功`);
|
||||
setNewTagValue("");
|
||||
setIsAddingTag(false);
|
||||
};
|
||||
|
||||
// 处理取消新增标签
|
||||
const handleCancelAddTag = () => {
|
||||
setNewTagValue("");
|
||||
setIsAddingTag(false);
|
||||
};
|
||||
|
||||
// 构建联系人或群聊详细信息
|
||||
const contractInfo = {
|
||||
name: contract.name || contract.nickname,
|
||||
nickname: contract.nickname,
|
||||
conRemark: remarkValue, // 使用当前编辑的备注值
|
||||
alias: contract.alias,
|
||||
wechatId: contract.wechatId,
|
||||
chatroomId: isGroup ? contract.chatroomId : undefined,
|
||||
chatroomOwner: isGroup ? contract.chatroomOwner : undefined,
|
||||
avatar: contract.avatar || contract.chatroomAvatar,
|
||||
phone: contract.phone || "-",
|
||||
email: contract.email || "-",
|
||||
department: contract.department || "-",
|
||||
position: contract.position || "-",
|
||||
company: contract.company || "-",
|
||||
region: contract.region || "-",
|
||||
joinDate: contract.joinDate || "-",
|
||||
notice: isGroup ? contract.notice : undefined,
|
||||
selfDisplyName: isGroup ? contract.selfDisplyName : undefined,
|
||||
status: "在线",
|
||||
tags: selectedTags,
|
||||
bio: contract.bio || contract.signature || "-",
|
||||
};
|
||||
|
||||
if (!showProfile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Sider width={320} className={styles.profileSider}>
|
||||
<div className={styles.profileContainer}>
|
||||
{/* 关闭按钮 */}
|
||||
<div className={styles.profileHeader}>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={onToggleProfile}
|
||||
className={styles.closeButton}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 头像和基本信息 */}
|
||||
<div className={styles.profileBasic}>
|
||||
<Avatar
|
||||
size={80}
|
||||
src={contractInfo.avatar}
|
||||
icon={<UserOutlined />}
|
||||
/>
|
||||
<div className={styles.profileInfo}>
|
||||
{isGroup && isEditingGroupName ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={groupNameValue}
|
||||
onChange={e => setGroupNameValue(e.target.value)}
|
||||
placeholder="请输入群名称"
|
||||
size="small"
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleSaveGroupName}
|
||||
style={{ color: "#52c41a" }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => {
|
||||
setGroupNameValue(contract.name || '');
|
||||
setIsEditingGroupName(false);
|
||||
}}
|
||||
style={{ color: "#ff4d4f" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Tooltip
|
||||
title={contractInfo.nickname || contractInfo.name}
|
||||
placement="top"
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<h4 className={styles.profileNickname}>
|
||||
{contractInfo.nickname || contractInfo.name}
|
||||
</h4>
|
||||
{isGroup && (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => setIsEditingGroupName(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div className={styles.profileStatus}>
|
||||
<span className={styles.statusDot}></span>
|
||||
{contractInfo.status}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 详细信息卡片 */}
|
||||
<Card title="详细信息" className={styles.profileCard}>
|
||||
{isGroup ? (
|
||||
// 群聊信息
|
||||
<>
|
||||
<div className={styles.infoItem}>
|
||||
<TeamOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>群ID:</span>
|
||||
<span className={styles.infoValue}>
|
||||
{contractInfo.chatroomId}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.infoItem}>
|
||||
<UserOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>群主:</span>
|
||||
<span className={styles.infoValue}>{contractInfo.chatroomOwner}</span>
|
||||
</div>
|
||||
<div className={styles.infoItem}>
|
||||
<UserOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>群昵称:</span>
|
||||
<div className={styles.infoValue}>
|
||||
{isEditingSelfDisplayName ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={selfDisplayNameValue}
|
||||
onChange={e => setSelfDisplayNameValue(e.target.value)}
|
||||
placeholder="请输入群昵称"
|
||||
size="small"
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleSaveSelfDisplayName}
|
||||
style={{ color: "#52c41a" }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => {
|
||||
setSelfDisplayNameValue(contract.selfDisplyName || '');
|
||||
setIsEditingSelfDisplayName(false);
|
||||
}}
|
||||
style={{ color: "#ff4d4f" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<span>{contractInfo.selfDisplyName || "点击添加群昵称"}</span>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => setIsEditingSelfDisplayName(true)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
// 好友信息
|
||||
<>
|
||||
<div className={styles.infoItem}>
|
||||
<TeamOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>微信号:</span>
|
||||
<span className={styles.infoValue}>
|
||||
{contractInfo.alias || contractInfo.wechatId}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.infoItem}>
|
||||
<PhoneOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>电话:</span>
|
||||
<span className={styles.infoValue}>{contractInfo.phone}</span>
|
||||
</div>
|
||||
<div className={styles.infoItem}>
|
||||
<EnvironmentOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>地区:</span>
|
||||
<span className={styles.infoValue}>{contractInfo.region}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!isGroup && (
|
||||
<div className={styles.infoItem}>
|
||||
<EditOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>备注:</span>
|
||||
<div className={styles.infoValue}>
|
||||
{isEditingRemark ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={remarkValue}
|
||||
onChange={e => setRemarkValue(e.target.value)}
|
||||
placeholder="请输入备注"
|
||||
size="small"
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleSaveRemark}
|
||||
style={{ color: "#52c41a" }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={handleCancelEdit}
|
||||
style={{ color: "#ff4d4f" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<span>{contractInfo.conRemark || "点击添加备注"}</span>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => setIsEditingRemark(true)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* 标签 - 仅在非群聊时显示 */}
|
||||
{!isGroup && (
|
||||
<Card title="标签" className={styles.profileCard}>
|
||||
<div className={styles.tagsContainer}>
|
||||
{/* 渲染所有可用标签,选中的排在前面 */}
|
||||
{[...new Set([...selectedTags, ...allAvailableTags])].map(
|
||||
(tag, index) => {
|
||||
const isSelected = selectedTags.includes(tag);
|
||||
return (
|
||||
<Tag
|
||||
key={index}
|
||||
color={isSelected ? "blue" : "default"}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: isSelected
|
||||
? "1px solid #1890ff"
|
||||
: "1px solid #d9d9d9",
|
||||
backgroundColor: isSelected ? "#e6f7ff" : "#fafafa",
|
||||
}}
|
||||
onClick={() => handleTagToggle(tag)}
|
||||
>
|
||||
{tag}
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
)}
|
||||
|
||||
{/* 新增标签区域 */}
|
||||
{isAddingTag ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
marginTop: "8px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={newTagValue}
|
||||
onChange={e => setNewTagValue(e.target.value)}
|
||||
placeholder="请输入标签名称"
|
||||
size="small"
|
||||
style={{ width: "120px" }}
|
||||
onPressEnter={handleAddTag}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleAddTag}
|
||||
style={{ color: "#52c41a" }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={handleCancelAddTag}
|
||||
style={{ color: "#ff4d4f" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Tag
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: "1px dashed #d9d9d9",
|
||||
backgroundColor: "#fafafa",
|
||||
}}
|
||||
onClick={() => setIsAddingTag(true)}
|
||||
>
|
||||
<PlusOutlined /> 新增标签
|
||||
</Tag>
|
||||
)}
|
||||
|
||||
{allAvailableTags.length === 0 && !isAddingTag && (
|
||||
<span style={{ color: "#999", fontSize: "12px" }}>
|
||||
暂无可用标签
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 个人简介或群公告 */}
|
||||
<Card title={isGroup ? "群公告" : "个人简介"} className={styles.profileCard}>
|
||||
{isGroup ? (
|
||||
// 群聊简介(原群公告)
|
||||
<div className={styles.infoValue}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.bioText}
|
||||
style={{
|
||||
maxHeight: "120px", // 约5行文本的高度
|
||||
overflowY: "auto", // 添加垂直滚动条
|
||||
paddingRight: "5px", // 为滚动条留出空间
|
||||
}}
|
||||
>
|
||||
{contractInfo.notice || "点击添加群公告"}
|
||||
</div>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={handleEditGroupNotice}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
// 个人简介
|
||||
<p className={styles.bioText}>{contractInfo.bio}</p>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className={styles.profileActions}>
|
||||
<Button type="primary" icon={<PhoneOutlined />} block>
|
||||
语音通话
|
||||
</Button>
|
||||
<Button
|
||||
icon={<VideoCameraOutlined />}
|
||||
block
|
||||
style={{ marginTop: 8 }}
|
||||
>
|
||||
视频通话
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Sider>
|
||||
|
||||
{/* 群公告编辑弹窗 */}
|
||||
<Modal
|
||||
title="发布群公告"
|
||||
open={isGroupNoticeModalVisible}
|
||||
onCancel={() => setIsGroupNoticeModalVisible(false)}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={() => setIsGroupNoticeModalVisible(false)}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
handleSaveGroupNotice();
|
||||
setIsGroupNoticeModalVisible(false);
|
||||
}}
|
||||
>
|
||||
确定
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Input.TextArea
|
||||
value={groupNoticeValue}
|
||||
onChange={e => setGroupNoticeValue(e.target.value)}
|
||||
placeholder="请输入内容"
|
||||
rows={6}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Person;
|
||||
@@ -168,6 +168,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.groupManagement {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.groupMemberList {
|
||||
.groupMember {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profileActions {
|
||||
margin-top: auto;
|
||||
padding-top: 16px;
|
||||
|
||||
@@ -25,6 +25,8 @@ import {
|
||||
CheckOutlined,
|
||||
PlusOutlined,
|
||||
NotificationOutlined,
|
||||
MinusOutlined,
|
||||
SwapOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
||||
@@ -65,6 +67,66 @@ const Person: React.FC<PersonProps> = ({
|
||||
const [isEditingSelfDisplayName, setIsEditingSelfDisplayName] = useState(false);
|
||||
const [selfDisplayNameValue, setSelfDisplayNameValue] = useState(contract.selfDisplyName || '');
|
||||
const [isGroupNoticeModalVisible, setIsGroupNoticeModalVisible] = useState(false);
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [groupMembers, setGroupMembers] = useState<any[]>([
|
||||
{
|
||||
"friendId": 17453058,
|
||||
"wechatId": "WANGMINGZHENG000",
|
||||
"nickname": "wong",
|
||||
"avatar": " https://wx.qlogo.cn/mmhead/ver_1/W67xuTtrYxgP80VoJGiceswibsxibqhrC06ib8Lh49pwV3ibibdqfXaDvRD4obSWdVibkdPOHWaedqviazEiasAiciaib3HJrvv7C0yyKtMosCbWxLcNWtXuN0WPicKKaTrQMe1NicQ20G/0 ",
|
||||
"isAdmin": false,
|
||||
"isDeleted": false,
|
||||
"deletedDate": "0001-01-01T00:00:00"
|
||||
},
|
||||
{
|
||||
"friendId": 10411197,
|
||||
"wechatId": "wxid_480es52qsj2812",
|
||||
"nickname": "老坑爹- 解放双手,释放时间",
|
||||
"avatar": " https://wx.qlogo.cn/mmhead/ver_1/r7xs2IJVd2Yhkd7yObdJzJkkiayOahjHNKtCZdiaECWyqR0XBqqlhE46UwT7hPZSf7QzplQiadVFuZtMIBL1LSliaJBK2azvL9PPyPT8MEHZQMA/0 ",
|
||||
"isAdmin": false,
|
||||
"isDeleted": false,
|
||||
"deletedDate": "0001-01-01T00:00:00"
|
||||
},
|
||||
{
|
||||
"friendId": 21168549,
|
||||
"wechatId": "wxid_dlhi90odctcl22",
|
||||
"nickname": "许永平",
|
||||
"avatar": " https://wx.qlogo.cn/mmhead/ver_1/KF8xrYxmYgHbdZpaPw78NFx7RibwWv1jsy5dlLEjgBDYkLBv0dadXSyLHicI218a34G3pMVhOsy1jA0QrDDKicSsYyGMzkdOT2xbwSpw0LKbvQ7yPtibJgrNPsbia0MYbHZXb/0 ",
|
||||
"isAdmin": false,
|
||||
"isDeleted": false,
|
||||
"deletedDate": "2025-08-26T15:42:54.9914073+08:00"
|
||||
}
|
||||
]);
|
||||
|
||||
const [hoveredMember, setHoveredMember] = useState(null);
|
||||
const [isAddFriendModalVisible, setIsAddFriendModalVisible] = useState(false);
|
||||
const [selectedMember, setSelectedMember] = useState(null);
|
||||
const [greeting, setGreeting] = useState("");
|
||||
|
||||
const handleAddFriend = (member) => {
|
||||
setSelectedMember(member);
|
||||
setGreeting(`你好, 我来自群聊${contractInfo.name}`);
|
||||
setIsAddFriendModalVisible(true);
|
||||
};
|
||||
|
||||
const handleSendFriendRequest = () => {
|
||||
if (!selectedMember) return;
|
||||
|
||||
sendCommand("CmdChatroomOperate", {
|
||||
chatroomOperateTyp: 1,
|
||||
extra: JSON.stringify({
|
||||
wechatId: selectedMember.wechatId,
|
||||
sendWord: greeting,
|
||||
}),
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
});
|
||||
|
||||
messageApi.success('好友请求已发送');
|
||||
setIsAddFriendModalVisible(false);
|
||||
setSelectedMember(null);
|
||||
setGreeting('');
|
||||
};
|
||||
|
||||
// 构建联系人或群聊详细信息
|
||||
|
||||
@@ -134,7 +196,7 @@ const Person: React.FC<PersonProps> = ({
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
chatroomOperateType: 6,
|
||||
extra: `{\"chatroomName\":\"${groupNameValue}\"}`,
|
||||
extra: `{"chatroomName":"${groupNameValue}"}`,
|
||||
});
|
||||
|
||||
messageApi.success("群名称修改成功");
|
||||
@@ -143,21 +205,26 @@ const Person: React.FC<PersonProps> = ({
|
||||
|
||||
// 点击编辑群名称按钮
|
||||
const handleEditGroupName = () => {
|
||||
setGroupNameValue(contract.name || '');
|
||||
setGroupNameValue(contractInfo.name || '');
|
||||
setIsEditingGroupName(true);
|
||||
};
|
||||
|
||||
// 处理群公告保存
|
||||
const handleSaveGroupNotice = () => {
|
||||
setConfirmLoading(true);
|
||||
sendCommand("CmdChatroomOperate", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
chatroomOperateType: 5,
|
||||
extra: `{\"announce\":\"${groupNoticeValue}\"}`,
|
||||
extra: `{"announce":"${groupNoticeValue}"}`,
|
||||
});
|
||||
|
||||
messageApi.success("群公告修改成功");
|
||||
setIsEditingGroupNotice(false);
|
||||
// 模拟延迟
|
||||
setTimeout(() => {
|
||||
messageApi.success("群公告修改成功");
|
||||
setIsGroupNoticeModalVisible(false);
|
||||
setConfirmLoading(false);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// 点击编辑群公告按钮
|
||||
@@ -273,6 +340,29 @@ const Person: React.FC<PersonProps> = ({
|
||||
setIsAddingTag(false);
|
||||
};
|
||||
|
||||
// 处理退出群聊
|
||||
const handleLeaveGroup = () => {
|
||||
Modal.confirm({
|
||||
title: "确定要退出群聊吗?",
|
||||
content: "退出后将不再接收此群聊消息。",
|
||||
okText: "确定",
|
||||
cancelText: "取消",
|
||||
onOk: () => {
|
||||
sendCommand("CmdChatroomOperate", {
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatChatroomId: contract.id,
|
||||
chatroomOperateType: 4, // 4 for quit
|
||||
extra: "",
|
||||
});
|
||||
messageApi.success("已退出群聊");
|
||||
// 可能还需要一个回调来关闭侧边栏或切换到另一个聊天
|
||||
if (onToggleProfile) {
|
||||
onToggleProfile();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 构建联系人或群聊详细信息
|
||||
const contractInfo = {
|
||||
name: contract.name || contract.nickname,
|
||||
@@ -317,71 +407,28 @@ const Person: React.FC<PersonProps> = ({
|
||||
</div>
|
||||
|
||||
{/* 头像和基本信息 */}
|
||||
<div className={styles.profileBasic}>
|
||||
<div className={styles.profileBasic} style={{ alignItems: "center" }}>
|
||||
<Avatar
|
||||
size={80}
|
||||
src={contractInfo.avatar}
|
||||
icon={<UserOutlined />}
|
||||
/>
|
||||
<div className={styles.profileInfo}>
|
||||
{isGroup && isEditingGroupName ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={groupNameValue}
|
||||
onChange={e => setGroupNameValue(e.target.value)}
|
||||
placeholder="请输入群名称"
|
||||
size="small"
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleSaveGroupName}
|
||||
style={{ color: "#52c41a" }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => {
|
||||
setGroupNameValue(contract.name || '');
|
||||
setIsEditingGroupName(false);
|
||||
}}
|
||||
style={{ color: "#ff4d4f" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Tooltip
|
||||
title={contractInfo.nickname || contractInfo.name}
|
||||
placement="top"
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<h4 className={styles.profileNickname}>
|
||||
{contractInfo.nickname || contractInfo.name}
|
||||
</h4>
|
||||
{isGroup && (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => setIsEditingGroupName(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div className={styles.profileInfo} style={{ textAlign: "center" }}>
|
||||
<div className={styles.profileStatus}>
|
||||
<span className={styles.statusDot}></span>
|
||||
{contractInfo.status}
|
||||
</div>
|
||||
<Tooltip
|
||||
title={contractInfo.nickname || contractInfo.name}
|
||||
placement="top"
|
||||
>
|
||||
<h4
|
||||
className={styles.profileNickname}
|
||||
style={{ margin: "8px 0 0" }}
|
||||
>
|
||||
{contractInfo.nickname || contractInfo.name}
|
||||
</h4>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -390,6 +437,72 @@ const Person: React.FC<PersonProps> = ({
|
||||
{isGroup ? (
|
||||
// 群聊信息
|
||||
<>
|
||||
<div className={styles.infoItem}>
|
||||
<TeamOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>群名称:</span>
|
||||
<div className={styles.infoValue}>
|
||||
{isEditingGroupName ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={groupNameValue}
|
||||
onChange={e => setGroupNameValue(e.target.value)}
|
||||
placeholder="请输入群名称"
|
||||
size="small"
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleSaveGroupName}
|
||||
style={{ color: "#52c41a" }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => {
|
||||
setGroupNameValue(contract.name || "");
|
||||
setIsEditingGroupName(false);
|
||||
}}
|
||||
style={{ color: "#ff4d4f" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={contractInfo.nickname || contractInfo.name}
|
||||
placement="top"
|
||||
>
|
||||
<h4
|
||||
className={styles.profileNickname}
|
||||
style={{ margin: 0, flexShrink: 0 }}
|
||||
>
|
||||
{contractInfo.nickname || contractInfo.name}
|
||||
</h4>
|
||||
</Tooltip>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={handleEditGroupName}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.infoItem}>
|
||||
<TeamOutlined className={styles.infoIcon} />
|
||||
<span className={styles.infoLabel}>群ID:</span>
|
||||
@@ -624,49 +737,39 @@ const Person: React.FC<PersonProps> = ({
|
||||
<Card title={isGroup ? "群公告" : "个人简介"} className={styles.profileCard}>
|
||||
{isGroup ? (
|
||||
// 群聊简介(原群公告)
|
||||
<div className={styles.infoValue}>
|
||||
{isEditingGroupNotice ? (
|
||||
<div
|
||||
className={styles.infoValue}
|
||||
onClick={() => {
|
||||
setGroupNoticeValue(contractInfo.notice || "");
|
||||
setIsGroupNoticeModalVisible(true);
|
||||
}}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.bioText}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
maxHeight: "120px",
|
||||
overflowY: "auto",
|
||||
paddingRight: "5px",
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Input.TextArea
|
||||
value={groupNoticeValue}
|
||||
onChange={e => setGroupNoticeValue(e.target.value)}
|
||||
placeholder="请输入内容"
|
||||
rows={6}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
{contractInfo.notice || "点击添加群公告"}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.bioText}
|
||||
style={{
|
||||
maxHeight: "120px", // 约5行文本的高度
|
||||
overflowY: "auto", // 添加垂直滚动条
|
||||
paddingRight: "5px", // 为滚动条留出空间
|
||||
}}
|
||||
>
|
||||
{contractInfo.notice || "点击添加群公告"}
|
||||
</div>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => setIsEditingGroupNotice(true)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
// 个人简介
|
||||
@@ -674,6 +777,59 @@ const Person: React.FC<PersonProps> = ({
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{isGroup && (
|
||||
<Card title="群成员" className={styles.profileCard}>
|
||||
<div className={styles.groupManagement}>
|
||||
<Button icon={<PlusOutlined />} onClick={() => setIsFriendSelectionVisible(true)}>添加成员</Button>
|
||||
<Button icon={<MinusOutlined />} onClick={() => setIsMemberSelectionVisible(true)}>删除成员</Button>
|
||||
<Button icon={<PlusOutlined />} onClick={() => setIsAdminSelectionVisible(true)}>添加管理</Button>
|
||||
<Button icon={<MinusOutlined />} onClick={() => setIsRemoveAdminSelectionVisible(true)}>删除管理</Button>
|
||||
<Button icon={<SwapOutlined />} onClick={() => setIsTransferOwnerSelectionVisible(true)}>转让群主</Button>
|
||||
</div>
|
||||
<div className={styles.groupMemberList}>
|
||||
{groupMembers.map(member => (
|
||||
<div
|
||||
key={member.friendId}
|
||||
className={styles.groupMember}
|
||||
onMouseEnter={() => setHoveredMember(member.friendId)}
|
||||
onMouseLeave={() => setHoveredMember(null)}
|
||||
>
|
||||
<Avatar size="small" src={member.avatar} />
|
||||
<span>{member.nickname}</span>
|
||||
{hoveredMember === member.friendId && (
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={() => handleAddFriend(member)}
|
||||
style={{ marginLeft: 'auto' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button type="link" danger block style={{ marginTop: '16px' }} onClick={handleLeaveGroup}>
|
||||
退出群聊
|
||||
</Button>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
title={`请求添加${selectedMember?.nickname}为好友`}
|
||||
visible={isAddFriendModalVisible}
|
||||
onOk={handleSendFriendRequest}
|
||||
onCancel={() => setIsAddFriendModalVisible(false)}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Input.TextArea
|
||||
value={greeting}
|
||||
onChange={(e) => setGreeting(e.target.value)}
|
||||
placeholder="请输入招呼语"
|
||||
rows={4}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className={styles.profileActions}>
|
||||
<Button type="primary" icon={<PhoneOutlined />} block>
|
||||
@@ -699,13 +855,11 @@ const Person: React.FC<PersonProps> = ({
|
||||
<Button key="cancel" onClick={() => setIsGroupNoticeModalVisible(false)}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
handleSaveGroupNotice();
|
||||
setIsGroupNoticeModalVisible(false);
|
||||
}}
|
||||
<Button
|
||||
key="submit"
|
||||
type="primary"
|
||||
loading={confirmLoading}
|
||||
onClick={handleSaveGroupNotice}
|
||||
>
|
||||
确定
|
||||
</Button>,
|
||||
@@ -716,7 +870,6 @@ const Person: React.FC<PersonProps> = ({
|
||||
onChange={e => setGroupNoticeValue(e.target.value)}
|
||||
placeholder="请输入内容"
|
||||
rows={6}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user