feat(微信聊天): 实现消息撤回功能并优化右键菜单
添加消息撤回API接口及状态管理 在右键菜单中根据消息发送时间和归属显示撤回选项 优化消息记录组件与状态管理的交互逻辑
This commit is contained in:
@@ -14,7 +14,7 @@ import AudioRecorder from "@/components/Upload/AudioRecorder";
|
||||
import ToContract from "./components/toContract";
|
||||
import ChatRecord from "./components/chatRecord";
|
||||
import styles from "./MessageEnter.module.scss";
|
||||
|
||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
||||
const { Footer } = Layout;
|
||||
const { TextArea } = Input;
|
||||
|
||||
@@ -27,6 +27,7 @@ const { sendCommand } = useWebSocketStore.getState();
|
||||
const MessageEnter: React.FC<MessageEnterProps> = ({ contract }) => {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [showMaterialModal, setShowMaterialModal] = useState(false);
|
||||
const EnterModule = useWeChatStore(state => state.EnterModule);
|
||||
|
||||
const handleSend = async () => {
|
||||
if (!inputValue.trim()) return;
|
||||
@@ -134,76 +135,81 @@ const MessageEnter: React.FC<MessageEnterProps> = ({ contract }) => {
|
||||
<>
|
||||
{/* 聊天输入 */}
|
||||
<Footer className={styles.chatFooter}>
|
||||
<div className={styles.inputContainer}>
|
||||
<div className={styles.inputToolbar}>
|
||||
<div className={styles.leftTool}>
|
||||
<EmojiPicker onEmojiSelect={handleEmojiSelect} />
|
||||
<SimpleFileUpload
|
||||
onFileUploaded={filePath =>
|
||||
handleFileUploaded(filePath, FileType.FILE)
|
||||
}
|
||||
maxSize={1}
|
||||
type={4}
|
||||
slot={
|
||||
<Button
|
||||
className={styles.toolbarButton}
|
||||
type="text"
|
||||
icon={<FolderOutlined />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<SimpleFileUpload
|
||||
onFileUploaded={filePath =>
|
||||
handleFileUploaded(filePath, FileType.IMAGE)
|
||||
}
|
||||
maxSize={1}
|
||||
type={1}
|
||||
slot={
|
||||
<Button
|
||||
className={styles.toolbarButton}
|
||||
type="text"
|
||||
icon={<PictureOutlined />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{["common"].includes(EnterModule) && (
|
||||
<div className={styles.inputContainer}>
|
||||
<div className={styles.inputToolbar}>
|
||||
<div className={styles.leftTool}>
|
||||
<EmojiPicker onEmojiSelect={handleEmojiSelect} />
|
||||
<SimpleFileUpload
|
||||
onFileUploaded={filePath =>
|
||||
handleFileUploaded(filePath, FileType.FILE)
|
||||
}
|
||||
maxSize={1}
|
||||
type={4}
|
||||
slot={
|
||||
<Button
|
||||
className={styles.toolbarButton}
|
||||
type="text"
|
||||
icon={<FolderOutlined />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<SimpleFileUpload
|
||||
onFileUploaded={filePath =>
|
||||
handleFileUploaded(filePath, FileType.IMAGE)
|
||||
}
|
||||
maxSize={1}
|
||||
type={1}
|
||||
slot={
|
||||
<Button
|
||||
className={styles.toolbarButton}
|
||||
type="text"
|
||||
icon={<PictureOutlined />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<AudioRecorder
|
||||
onAudioUploaded={audioData =>
|
||||
handleFileUploaded(audioData, FileType.AUDIO)
|
||||
}
|
||||
className={styles.toolbarButton}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.rightTool}>
|
||||
<ToContract className={styles.rightToolItem} />
|
||||
<ChatRecord className={styles.rightToolItem} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.inputArea}>
|
||||
<div className={styles.inputWrapper}>
|
||||
<TextArea
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e.target.value)}
|
||||
onKeyDown={handleKeyPress}
|
||||
placeholder="输入消息..."
|
||||
className={styles.messageInput}
|
||||
autoSize={{ minRows: 2, maxRows: 6 }}
|
||||
/>
|
||||
<div className={styles.sendButtonArea}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SendOutlined />}
|
||||
onClick={handleSend}
|
||||
disabled={!inputValue.trim()}
|
||||
className={styles.sendButton}
|
||||
>
|
||||
发送
|
||||
</Button>
|
||||
<AudioRecorder
|
||||
onAudioUploaded={audioData =>
|
||||
handleFileUploaded(audioData, FileType.AUDIO)
|
||||
}
|
||||
className={styles.toolbarButton}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.rightTool}>
|
||||
<ToContract className={styles.rightToolItem} />
|
||||
<ChatRecord className={styles.rightToolItem} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.inputArea}>
|
||||
<div className={styles.inputWrapper}>
|
||||
<TextArea
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e.target.value)}
|
||||
onKeyDown={handleKeyPress}
|
||||
placeholder="输入消息..."
|
||||
className={styles.messageInput}
|
||||
autoSize={{ minRows: 2, maxRows: 6 }}
|
||||
/>
|
||||
<div className={styles.sendButtonArea}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SendOutlined />}
|
||||
onClick={handleSend}
|
||||
disabled={!inputValue.trim()}
|
||||
className={styles.sendButton}
|
||||
>
|
||||
发送
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.inputHint}>
|
||||
按下Ctrl+Enter换行,Enter发送
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.inputHint}>按下Ctrl+Enter换行,Enter发送</div>
|
||||
</div>
|
||||
)}
|
||||
{/* {["common"].includes(EnterModule) &&} */}
|
||||
</Footer>
|
||||
|
||||
{/* 素材选择模态框 */}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 朋友圈相关的API接口
|
||||
import { useWebSocketStore } from "@/store/module/websocket/websocket";
|
||||
|
||||
// 朋友圈请求参数接口
|
||||
export interface FetchMomentParams {
|
||||
friendMessageId: number;
|
||||
chatroomMessageId: number;
|
||||
seq: number;
|
||||
}
|
||||
|
||||
// 获取朋友圈数据
|
||||
export const fetchReCallApi = async (params: FetchMomentParams) => {
|
||||
const { sendCommand } = useWebSocketStore.getState();
|
||||
sendCommand("CmdRecallMessage", params);
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ExportOutlined,
|
||||
LinkOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import dayjs from "dayjs";
|
||||
import { ChatRecord } from "@/pages/pc/ckbox/data";
|
||||
import styles from "./ClickMenu.module.scss";
|
||||
|
||||
@@ -17,6 +18,7 @@ interface ClickMenuProps {
|
||||
messageData: ChatRecord | null;
|
||||
onClose: () => void;
|
||||
onCommad: (action: string) => void;
|
||||
isOwn: boolean;
|
||||
}
|
||||
|
||||
const ClickMenu: React.FC<ClickMenuProps> = ({
|
||||
@@ -26,6 +28,7 @@ const ClickMenu: React.FC<ClickMenuProps> = ({
|
||||
messageData,
|
||||
onClose,
|
||||
onCommad,
|
||||
isOwn,
|
||||
}) => {
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const [position, setPosition] = useState({ x, y });
|
||||
@@ -96,7 +99,18 @@ const ClickMenu: React.FC<ClickMenuProps> = ({
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
// 检查是否显示撤回功能
|
||||
const isShowRecall = (): boolean => {
|
||||
// 早期返回:非自己发送的消息不能撤回
|
||||
if (!isOwn) return false;
|
||||
// 使用 dayjs 计算时间差,1.8分钟 = 108秒
|
||||
const timeDiffInSeconds = dayjs().diff(
|
||||
dayjs(messageData.wechatTime),
|
||||
"second",
|
||||
);
|
||||
|
||||
return timeDiffInSeconds <= 108;
|
||||
};
|
||||
const menuItems = [
|
||||
{
|
||||
key: "transmit",
|
||||
@@ -118,11 +132,15 @@ const ClickMenu: React.FC<ClickMenuProps> = ({
|
||||
icon: <LinkOutlined />,
|
||||
label: "引用",
|
||||
},
|
||||
{
|
||||
key: "recall",
|
||||
icon: <RollbackOutlined />,
|
||||
label: "撤回",
|
||||
},
|
||||
...(isShowRecall()
|
||||
? [
|
||||
{
|
||||
key: "recall",
|
||||
icon: <RollbackOutlined />,
|
||||
label: "撤回",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -144,6 +162,7 @@ const ClickMenu: React.FC<ClickMenuProps> = ({
|
||||
handleCopy();
|
||||
} else {
|
||||
onCommad(value.key);
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
}))}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getEmojiPath } from "@/components/EmojiSeclection/wechatEmoji";
|
||||
import styles from "./MessageRecord.module.scss";
|
||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
||||
import { fetchReCallApi } from "./api";
|
||||
interface MessageRecordProps {
|
||||
contract: ContractData | weChatGroup;
|
||||
}
|
||||
@@ -23,11 +24,12 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
y: 0,
|
||||
messageData: null as ChatRecord | null,
|
||||
});
|
||||
|
||||
const [nowIsOwn, setNowIsOwn] = useState(false);
|
||||
// 选中的聊天记录状态
|
||||
const [selectedRecords, setSelectedRecords] = useState<ChatRecord[]>([]);
|
||||
|
||||
const currentMessages = useWeChatStore(state => state.currentMessages);
|
||||
|
||||
const loadChatMessages = useWeChatStore(state => state.loadChatMessages);
|
||||
const messagesLoading = useWeChatStore(state => state.messagesLoading);
|
||||
const isLoadingData = useWeChatStore(state => state.isLoadingData);
|
||||
@@ -39,8 +41,9 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
const updateShowCheckbox = useWeChatStore(state => state.updateShowCheckbox);
|
||||
const updateEnterModule = useWeChatStore(state => state.updateEnterModule);
|
||||
const currentKf = useCkChatStore(state =>
|
||||
state.kfUserList.find(kf => kf.id === state.kfSelected),
|
||||
state.kfUserList.find(kf => kf.id === contract.wechatAccountId),
|
||||
);
|
||||
|
||||
// 判断是否为表情包URL的工具函数
|
||||
const isEmojiUrl = (content: string): boolean => {
|
||||
return (
|
||||
@@ -471,7 +474,11 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
};
|
||||
|
||||
// 右键菜单事件处理
|
||||
const handleContextMenu = (e: React.MouseEvent, msg: ChatRecord) => {
|
||||
const handleContextMenu = (
|
||||
e: React.MouseEvent,
|
||||
msg: ChatRecord,
|
||||
isOwn: boolean,
|
||||
) => {
|
||||
e.preventDefault();
|
||||
setContextMenu({
|
||||
visible: true,
|
||||
@@ -479,6 +486,7 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
y: e.clientY,
|
||||
messageData: msg,
|
||||
});
|
||||
setNowIsOwn(isOwn);
|
||||
};
|
||||
|
||||
const handleCloseContextMenu = () => {
|
||||
@@ -490,26 +498,6 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleCopyMessage = (content: string) => {
|
||||
// 复制消息内容的处理逻辑
|
||||
console.log("复制消息:", content);
|
||||
};
|
||||
|
||||
const handleDeleteMessage = (messageId: string) => {
|
||||
// 删除消息的处理逻辑
|
||||
console.log("删除消息:", messageId);
|
||||
};
|
||||
|
||||
const handleForwardMessage = (messageData: ChatRecord) => {
|
||||
// 转发消息的处理逻辑
|
||||
console.log("转发消息:", messageData);
|
||||
};
|
||||
|
||||
const handleRetryMessage = (messageData: ChatRecord) => {
|
||||
// 重试发送消息的处理逻辑
|
||||
console.log("重试发送:", messageData);
|
||||
};
|
||||
|
||||
// 处理checkbox选中状态变化
|
||||
const handleCheckboxChange = (checked: boolean, msg: ChatRecord) => {
|
||||
if (checked) {
|
||||
@@ -549,7 +537,7 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
className={`${styles.messageItem} ${
|
||||
isOwn ? styles.ownMessage : styles.otherMessage
|
||||
}`}
|
||||
onContextMenu={e => isOwn && handleContextMenu(e, msg)}
|
||||
onContextMenu={e => handleContextMenu(e, msg, isOwn)}
|
||||
>
|
||||
<div className={styles.messageContent}>
|
||||
{/* 如果不是群聊 */}
|
||||
@@ -667,6 +655,30 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
|
||||
loadChatMessages(false, timestamp);
|
||||
};
|
||||
|
||||
const handleDeleteMessage = (messageId: string) => {
|
||||
// 删除消息的处理逻辑
|
||||
console.log("删除消息:", messageId);
|
||||
};
|
||||
|
||||
const handleForwardMessage = (messageData: ChatRecord) => {
|
||||
// 转发消息的处理逻辑
|
||||
console.log("转发消息:", messageData);
|
||||
};
|
||||
|
||||
const handleRetryMessage = (messageData: ChatRecord) => {
|
||||
// 重试发送消息的处理逻辑
|
||||
console.log("重试发送:", messageData);
|
||||
};
|
||||
const handRecall = messageData => {
|
||||
// 撤回消息的处理逻辑
|
||||
fetchReCallApi({
|
||||
friendMessageId: messageData?.wechatFriendId ? messageData.id : 0,
|
||||
chatroomMessageId: messageData?.wechatFriendId ? 0 : messageData.id,
|
||||
seq: +new Date(),
|
||||
});
|
||||
};
|
||||
|
||||
const handCommad = (action: string) => {
|
||||
switch (action) {
|
||||
case "transmit":
|
||||
@@ -682,6 +694,7 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
break;
|
||||
case "recall":
|
||||
// 撤回逻辑
|
||||
handRecall(contextMenu.messageData);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -720,6 +733,7 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
x={contextMenu.x}
|
||||
y={contextMenu.y}
|
||||
messageData={contextMenu.messageData}
|
||||
isOwn={nowIsOwn}
|
||||
onClose={handleCloseContextMenu}
|
||||
onCommad={handCommad}
|
||||
/>
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
=======================发送请求操作============================
|
||||
//我的朋友圈
|
||||
{
|
||||
"cmdType": "CmdFetchMoment",
|
||||
"wechatAccountId": 300745,
|
||||
"wechatFriendId": 0,
|
||||
"createTimeSec": 1758010384,
|
||||
"prevSnsId": 0,
|
||||
"count": 10,
|
||||
"isTimeline": false,
|
||||
"seq": 3
|
||||
}
|
||||
|
||||
//朋友圈广场
|
||||
|
||||
{
|
||||
"cmdType": "CmdFetchMoment",
|
||||
"wechatAccountId": 300745,
|
||||
"wechatFriendId": 0,
|
||||
"createTimeSec": 1758010499,
|
||||
"prevSnsId": 0,
|
||||
"count": 10,
|
||||
"isTimeline": true,
|
||||
"seq": 8
|
||||
}
|
||||
|
||||
//取消点赞
|
||||
{
|
||||
"cmdType": "CmdMomentCancelInteract",
|
||||
"optType": 1,
|
||||
"wechatAccountId": 300745,
|
||||
"wechatFriendId": 0,
|
||||
"CommentId2": "",
|
||||
"CommentTime": 0,
|
||||
"snsId": "-3699481452547067269",
|
||||
"seq": 4
|
||||
}
|
||||
|
||||
//点赞
|
||||
{
|
||||
"cmdType": "CmdMomentInteract",
|
||||
"momentInteractType": 1,
|
||||
"wechatAccountId": 300745,
|
||||
"wechatFriendId": 0,
|
||||
"snsId": "-3699481452547067269",
|
||||
"seq": 7
|
||||
}
|
||||
|
||||
//指定好友朋朋友圈
|
||||
{
|
||||
"cmdType": "CmdFetchMoment",
|
||||
"wechatAccountId": 300745,
|
||||
"wechatFriendId": 21168549,
|
||||
"createTimeSec": 1758011261,
|
||||
"prevSnsId": 0,
|
||||
"count": 10,
|
||||
"isTimeline": false,
|
||||
"seq": 24
|
||||
}
|
||||
|
||||
//发布评论
|
||||
{
|
||||
"cmdType":"CmdMomentInteract",
|
||||
"wechatAccountId":300745,
|
||||
"wechatFriendId":21168549,
|
||||
"snsId":"-3705790026851937712",
|
||||
"sendWord":"测试评论",
|
||||
"momentInteractType":2,
|
||||
"seq":27
|
||||
}
|
||||
|
||||
//撤销评论
|
||||
{
|
||||
"cmdType": "CmdMomentCancelInteract",
|
||||
"optType": 2,
|
||||
"wechatAccountId": 300745,
|
||||
"wechatFriendId": 21168549,
|
||||
"CommentId2": "",
|
||||
"CommentTime": 1758011328.377,
|
||||
"snsId": "-3705790026851937712",
|
||||
"seq": 30
|
||||
}
|
||||
=======================数据接收格式============================
|
||||
//我的朋友圈接收的数据
|
||||
{
|
||||
"wechatFriendId": 0,
|
||||
"wechatAccountId": 300745,
|
||||
"result": [{
|
||||
"commentList": [],
|
||||
"createTime": 1758011600,
|
||||
"likeList": [],
|
||||
"momentEntity": {
|
||||
"content": "回家第一时间宠幸露丝大小姐",
|
||||
"createTime": 1758011600,
|
||||
"lat": 0,
|
||||
"lng": 0,
|
||||
"location": "",
|
||||
"objectType": 1,
|
||||
"picSize": 0,
|
||||
"resUrls": [
|
||||
"https://ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com/weremote/chat-logs/5E2C38F5A275450D935F3ECEC076124E/aa6d4c2f7b1fe24d04d34f4f409883e6/sns/wxid_480es52qsj2812/-3699473895763463557/-3699473895763463557-14747270178949771902.jpg", "https://ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com/weremote/chat-logs/5E2C38F5A275450D935F3ECEC076124E/aa6d4c2f7b1fe24d04d34f4f409883e6/sns/wxid_480es52qsj2812/-3699473895763463557/-3699473895763463557-14747270179007509101.jpg", "https://ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com/weremote/chat-logs/5E2C38F5A275450D935F3ECEC076124E/aa6d4c2f7b1fe24d04d34f4f409883e6/sns/wxid_480es52qsj2812/-3699473895763463557/-3699473895763463557-14747270179101684338.jpg", "https://ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com/weremote/chat-logs/5E2C38F5A275450D935F3ECEC076124E/aa6d4c2f7b1fe24d04d34f4f409883e6/sns/wxid_480es52qsj2812/-3699473895763463557/-3699473895763463557-14747270179158569581.jpg"
|
||||
],
|
||||
"snsId": "-3699473895763463557",
|
||||
"urls": ["/sns/c/4/snst_14747270178949771902", "/sns/9/1/snst_14747270179007509101", "/sns/0/c/snst_14747270179101684338", "/sns/3/c/snst_14747270179158569581"],
|
||||
"userName": "wxid_480es52qsj2812"
|
||||
},
|
||||
"snsId": "-3699473895763463557",
|
||||
"type": 1
|
||||
}],
|
||||
"seq": 1097555,
|
||||
"cmdType": "CmdFetchMomentResult"
|
||||
}
|
||||
|
||||
//朋友圈广场接收的数据
|
||||
{
|
||||
"wechatFriendId": 0,
|
||||
"wechatAccountId": 300745,
|
||||
"result": [{
|
||||
"commentList": [],
|
||||
"createTime": 1758018895,
|
||||
"likeList": [],
|
||||
"momentEntity": {
|
||||
"content": "还下雨不下雨,",
|
||||
"createTime": 1758018895,
|
||||
"lat": 0,
|
||||
"lng": 0,
|
||||
"location": "",
|
||||
"objectType": 15,
|
||||
"picSize": 0,
|
||||
"resUrls": [],
|
||||
"snsId": "-3699412700395531708",
|
||||
"urls": ["https://ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com/weremote/chat-logs/5E2C38F5A275450D935F3ECEC076124E/aa6d4c2f7b1fe24d04d34f4f409883e6/sns/wxid_480es52qsj2812/-3699412700395531708/-3699412700395531708.jpg"],
|
||||
"userName": "Danrtsey_DX"
|
||||
},
|
||||
"snsId": "-3699412700395531708",
|
||||
"type": 15
|
||||
}],
|
||||
"seq": 1098203,
|
||||
"cmdType": "CmdFetchMomentResult"
|
||||
}
|
||||
|
||||
//指定好友的朋友圈接收的数据
|
||||
{
|
||||
"wechatFriendId": 21168549,
|
||||
"wechatAccountId": 300745,
|
||||
"result": [{
|
||||
"commentList": [],
|
||||
"createTime": 1757258659,
|
||||
"likeList": [{
|
||||
"createTime": 1757315556,
|
||||
"nickName": "老坑爹- 解放双手,释放时间",
|
||||
"wechatId": "wxid_480es52qsj2812"
|
||||
}],
|
||||
"momentEntity": {
|
||||
"content": "没看到血月[旺柴]",
|
||||
"createTime": 1757258659,
|
||||
"lat": 0,
|
||||
"lng": 0,
|
||||
"location": "",
|
||||
"objectType": 1,
|
||||
"picSize": 0,
|
||||
"resUrls": ["https://ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com/weremote/chat-logs/5E2C38F5A275450D935F3ECEC076124E/aa6d4c2f7b1fe24d04d34f4f409883e6/sns/wxid_480es52qsj2812/-3705790026851937712/-3705790026851937712-14740954047910318662.jpg"],
|
||||
"snsId": "-3705790026851937712",
|
||||
"urls": ["/sns/3/3/snst_14740954047910318662"],
|
||||
"userName": "wxid_dlhi90odctcl22"
|
||||
},
|
||||
"snsId": "-3705790026851937712",
|
||||
"type": 1
|
||||
}],
|
||||
"seq": 1098348,
|
||||
"cmdType": "CmdFetchMomentResult"
|
||||
}
|
||||
@@ -17,6 +17,12 @@ export interface WeChatState {
|
||||
|
||||
// 当前聊天用户的消息列表(只存储当前聊天用户的消息)
|
||||
currentMessages: ChatRecord[];
|
||||
// 添加消息
|
||||
addMessage: (message: ChatRecord) => void;
|
||||
// 替换消息
|
||||
updateMessage: (messageId: number, updates: Partial<ChatRecord>) => void;
|
||||
// 撤回消息
|
||||
recallMessage: (messageId: number) => void;
|
||||
// 消息加载状态
|
||||
messagesLoading: boolean;
|
||||
isLoadingData: boolean;
|
||||
@@ -48,6 +54,5 @@ export interface WeChatState {
|
||||
// 视频消息处理方法
|
||||
setVideoLoading: (messageId: number, isLoading: boolean) => void;
|
||||
setVideoUrl: (messageId: number, videoUrl: string) => void;
|
||||
addMessage: (message: ChatRecord) => void;
|
||||
receivedMsg: (message: ChatRecord) => void;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,29 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
// 初始状态
|
||||
currentContract: null,
|
||||
currentMessages: [],
|
||||
//添加消息
|
||||
addMessage: message => {
|
||||
set(state => ({
|
||||
currentMessages: [...state.currentMessages, message],
|
||||
}));
|
||||
},
|
||||
//替换消息
|
||||
updateMessage: (messageId, updates) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg =>
|
||||
msg.id === messageId ? { ...msg, ...updates } : msg,
|
||||
),
|
||||
}));
|
||||
},
|
||||
//撤回消息
|
||||
recallMessage: (messageId: number) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.filter(
|
||||
msg => msg.id !== messageId,
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
messagesLoading: false,
|
||||
isLoadingData: false,
|
||||
currentGroupMembers: [],
|
||||
@@ -32,7 +55,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
updateShowCheckbox: (show: boolean) => {
|
||||
set({ showCheckbox: show });
|
||||
},
|
||||
EnterModule: "common",
|
||||
EnterModule: "common", //common | multipleForwarding
|
||||
updateEnterModule: (module: string) => {
|
||||
set({ EnterModule: module });
|
||||
},
|
||||
@@ -171,12 +194,6 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
set({ messagesLoading: Boolean(loading) });
|
||||
},
|
||||
|
||||
addMessage: message => {
|
||||
set(state => ({
|
||||
currentMessages: [...state.currentMessages, message],
|
||||
}));
|
||||
},
|
||||
|
||||
receivedMsg: async message => {
|
||||
const currentContract = useWeChatStore.getState().currentContract;
|
||||
//判断群还是好友
|
||||
@@ -215,14 +232,6 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}
|
||||
},
|
||||
|
||||
updateMessage: (messageId, updates) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg =>
|
||||
msg.id === messageId ? { ...msg, ...updates } : msg,
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
// 便捷选择器
|
||||
getCurrentContact: () => get().currentContract,
|
||||
getCurrentMessages: () => get().currentMessages,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { db } from "@/utils/db";
|
||||
// 消息处理器类型定义
|
||||
type MessageHandler = (message: WebSocketMessage) => void;
|
||||
const addMessage = useWeChatStore.getState().addMessage;
|
||||
const recallMessage = useWeChatStore.getState().recallMessage;
|
||||
const receivedMsg = useWeChatStore.getState().receivedMsg;
|
||||
const updateMomentCommonLoading =
|
||||
useWeChatStore.getState().updateMomentCommonLoading;
|
||||
@@ -92,6 +93,18 @@ const messageHandlers: Record<string, MessageHandler> = {
|
||||
}
|
||||
},
|
||||
|
||||
//撤回消息
|
||||
CmdMessageRecalled: message => {
|
||||
const MessageId = message.friendMessageId || message.chatroomMessageId;
|
||||
recallMessage(MessageId);
|
||||
// {
|
||||
// "friendMessageId": 745007874,
|
||||
// "chatroomMessageId": 0,
|
||||
// "seq": 2076470,
|
||||
// "cmdType": "CmdMessageRecalled"
|
||||
// }
|
||||
},
|
||||
|
||||
// 可以继续添加更多处理器...
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user