refactor(TransmitModal): 重构转发模态框组件并集成到消息记录
- 删除旧版转发模态框组件及相关文件 - 实现新版转发模态框组件,支持联系人搜索和多选 - 集成转发功能到消息记录组件 - 更新状态管理以支持转发功能 - 优化样式和响应式设计
This commit is contained in:
@@ -1,95 +0,0 @@
|
||||
# TransmitModal 转发模态框组件
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🔍 支持联系人搜索(姓名和拼音)
|
||||
- 👥 支持个人和群组联系人
|
||||
- ✅ 多选功能,可设置最大选择数量
|
||||
- 📱 响应式设计,适配移动端
|
||||
- 🎨 微信风格UI设计
|
||||
- ♿ 无障碍支持
|
||||
|
||||
## 使用方法
|
||||
|
||||
```tsx
|
||||
import TransmitModal, { Contact } from './components/TransmitModal';
|
||||
|
||||
const contacts: Contact[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: '张三',
|
||||
avatar: 'https://example.com/avatar1.jpg',
|
||||
type: 'user',
|
||||
pinyin: 'zhangsan'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '开发群',
|
||||
type: 'group',
|
||||
pinyin: 'kaifaqun'
|
||||
}
|
||||
];
|
||||
|
||||
function App() {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const handleConfirm = (selectedContacts: Contact[]) => {
|
||||
console.log('选择的联系人:', selectedContacts);
|
||||
// 执行转发逻辑
|
||||
setShowModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<TransmitModal
|
||||
open={showModal}
|
||||
onCancel={() => setShowModal(false)}
|
||||
onConfirm={handleConfirm}
|
||||
contacts={contacts}
|
||||
title="转发"
|
||||
maxSelection={9}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| open | boolean | - | 是否显示模态框 |
|
||||
| onCancel | () => void | - | 取消回调 |
|
||||
| onConfirm | (contacts: Contact[]) => void | - | 确认回调 |
|
||||
| contacts | Contact[] | - | 联系人列表 |
|
||||
| title | string | '转发' | 模态框标题 |
|
||||
| confirmText | string | '确定' | 确认按钮文本 |
|
||||
| cancelText | string | '取消' | 取消按钮文本 |
|
||||
| maxSelection | number | 9 | 最大选择数量 |
|
||||
|
||||
## Contact 接口
|
||||
|
||||
```tsx
|
||||
interface Contact {
|
||||
id: string; // 唯一标识
|
||||
name: string; // 显示名称
|
||||
avatar?: string; // 头像URL
|
||||
type: 'user' | 'group'; // 类型:用户或群组
|
||||
pinyin?: string; // 拼音,用于搜索
|
||||
}
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件使用 CSS Modules,可以通过覆盖以下类名来定制样式:
|
||||
|
||||
- `.transmitModal` - 模态框容器
|
||||
- `.contactList` - 左侧联系人列表
|
||||
- `.selectedList` - 右侧已选择列表
|
||||
- `.contactItem` - 联系人项
|
||||
- `.selectedItem` - 已选择项
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保传入的 `contacts` 数组中每个联系人的 `id` 唯一
|
||||
2. `pinyin` 字段可选,但建议提供以支持拼音搜索
|
||||
3. 组件会自动处理选择状态,无需外部维护
|
||||
4. 达到最大选择数量时,未选择的联系人会被禁用
|
||||
@@ -1,26 +1,12 @@
|
||||
// TransmitModal 组件样式 - 微信风格
|
||||
.transmitModal {
|
||||
:global(.ant-modal-content) {
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
.ant-modal-content {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
height: calc(500px - 110px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-modal-header) {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:global(.ant-modal-body) {
|
||||
padding: 0;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
:global(.ant-modal-footer) {
|
||||
padding: 12px 20px;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
text-align: right;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,96 +17,65 @@
|
||||
}
|
||||
|
||||
.searchContainer {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
background: #fafafa;
|
||||
}
|
||||
margin-bottom: 16px;
|
||||
|
||||
.searchInput {
|
||||
border-radius: 20px;
|
||||
|
||||
:global(.ant-input) {
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
.searchInput {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.contentBody {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.contactList {
|
||||
flex: 1;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.contactList,
|
||||
.selectedList {
|
||||
width: 200px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.listHeader {
|
||||
padding: 12px 16px;
|
||||
background: #f0f0f0;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
font-size: 14px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.listContent {
|
||||
flex: 1;
|
||||
height: 60vh;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
.contactItem,
|
||||
.selectedItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 4px;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #d9d9d9;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: #bfbfbf;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.contactItem {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
:global(.ant-checkbox-wrapper) {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:global(.ant-checkbox) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
:global(.ant-checkbox-disabled) {
|
||||
:global(.ant-checkbox-inner) {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
}
|
||||
.selectedItem {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.contactInfo {
|
||||
@@ -128,109 +83,75 @@
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.contactName {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
color: #262626;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.groupIcon {
|
||||
color: #1890ff;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.selectedItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
margin: 4px 8px;
|
||||
background: #ffffff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e8e8e8;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #d9d9d9;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.removeIcon {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
color: #8c8c8c;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 2px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: #ff4d4f;
|
||||
background: #fff2f0;
|
||||
background-color: #fff2f0;
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态样式
|
||||
:global(.ant-empty) {
|
||||
margin: 40px 0;
|
||||
|
||||
:global(.ant-empty-description) {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
.loadingContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.errorContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.transmitModal {
|
||||
:global(.ant-modal) {
|
||||
width: 90% !important;
|
||||
max-width: none;
|
||||
.ant-modal-content {
|
||||
max-height: 600px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
max-height: calc(600px - 110px);
|
||||
}
|
||||
}
|
||||
|
||||
.contentBody {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.contactList,
|
||||
.selectedList {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.contactName {
|
||||
font-size: 13px;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮样式优化
|
||||
:global(.ant-btn-primary) {
|
||||
background: #07c160;
|
||||
border-color: #07c160;
|
||||
|
||||
&:hover {
|
||||
background: #06ad56;
|
||||
border-color: #06ad56;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: #f5f5f5;
|
||||
border-color: #d9d9d9;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
}
|
||||
|
||||
// 复选框样式优化
|
||||
:global(.ant-checkbox-checked) {
|
||||
:global(.ant-checkbox-inner) {
|
||||
background-color: #07c160;
|
||||
border-color: #07c160;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.ant-checkbox-wrapper:hover) {
|
||||
:global(.ant-checkbox-inner) {
|
||||
border-color: #07c160;
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { Modal, Input, Button, Avatar, Checkbox, Empty } from "antd";
|
||||
import {
|
||||
SearchOutlined,
|
||||
CloseOutlined,
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import styles from "./TransmitModal.module.scss";
|
||||
|
||||
export interface Contact {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
type: "user" | "group";
|
||||
pinyin?: string; // 用于搜索
|
||||
}
|
||||
|
||||
export interface TransmitModalProps {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onConfirm: (selectedContacts: Contact[]) => void;
|
||||
contacts: Contact[];
|
||||
title?: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
maxSelection?: number;
|
||||
}
|
||||
|
||||
const TransmitModal: React.FC<TransmitModalProps> = ({
|
||||
open,
|
||||
onCancel,
|
||||
onConfirm,
|
||||
contacts,
|
||||
title = "转发",
|
||||
confirmText = "确定",
|
||||
cancelText = "取消",
|
||||
maxSelection = 9,
|
||||
}) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [selectedContacts, setSelectedContacts] = useState<Contact[]>([]);
|
||||
|
||||
// 重置状态
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setSearchValue("");
|
||||
setSelectedContacts([]);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
// 过滤联系人
|
||||
const filteredContacts = useMemo(() => {
|
||||
if (!searchValue.trim()) return contacts;
|
||||
|
||||
const keyword = searchValue.toLowerCase();
|
||||
return contacts.filter(
|
||||
contact =>
|
||||
contact.name.toLowerCase().includes(keyword) ||
|
||||
contact.pinyin?.toLowerCase().includes(keyword),
|
||||
);
|
||||
}, [contacts, searchValue]);
|
||||
|
||||
// 处理联系人选择
|
||||
const handleContactSelect = (contact: Contact, checked: boolean) => {
|
||||
if (checked) {
|
||||
if (selectedContacts.length >= maxSelection) {
|
||||
return; // 达到最大选择数量
|
||||
}
|
||||
setSelectedContacts(prev => [...prev, contact]);
|
||||
} else {
|
||||
setSelectedContacts(prev => prev.filter(item => item.id !== contact.id));
|
||||
}
|
||||
};
|
||||
|
||||
// 移除已选择的联系人
|
||||
const handleRemoveSelected = (contactId: string) => {
|
||||
setSelectedContacts(prev => prev.filter(item => item.id !== contactId));
|
||||
};
|
||||
|
||||
// 确认转发
|
||||
const handleConfirm = () => {
|
||||
onConfirm(selectedContacts);
|
||||
};
|
||||
|
||||
// 检查联系人是否已选择
|
||||
const isContactSelected = (contactId: string) => {
|
||||
return selectedContacts.some(contact => contact.id === contactId);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
width={600}
|
||||
height={500}
|
||||
className={styles.transmitModal}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={onCancel}>
|
||||
{cancelText}
|
||||
</Button>,
|
||||
<Button
|
||||
key="confirm"
|
||||
type="primary"
|
||||
onClick={handleConfirm}
|
||||
disabled={selectedContacts.length === 0}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<div className={styles.modalContent}>
|
||||
{/* 搜索框 */}
|
||||
<div className={styles.searchContainer}>
|
||||
<Input
|
||||
placeholder="输入联系人或群名"
|
||||
prefix={<SearchOutlined />}
|
||||
value={searchValue}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
className={styles.searchInput}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.contentBody}>
|
||||
{/* 左侧联系人列表 */}
|
||||
<div className={styles.contactList}>
|
||||
<div className={styles.listHeader}>
|
||||
<span>联系人</span>
|
||||
</div>
|
||||
<div className={styles.listContent}>
|
||||
{filteredContacts.length > 0 ? (
|
||||
filteredContacts.map(contact => (
|
||||
<div key={contact.id} className={styles.contactItem}>
|
||||
<Checkbox
|
||||
checked={isContactSelected(contact.id)}
|
||||
onChange={e =>
|
||||
handleContactSelect(contact, e.target.checked)
|
||||
}
|
||||
disabled={
|
||||
!isContactSelected(contact.id) &&
|
||||
selectedContacts.length >= maxSelection
|
||||
}
|
||||
>
|
||||
<div className={styles.contactInfo}>
|
||||
<Avatar
|
||||
size={32}
|
||||
src={contact.avatar}
|
||||
icon={
|
||||
contact.type === "group" ? (
|
||||
<TeamOutlined />
|
||||
) : (
|
||||
<UserOutlined />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className={styles.contactName}>
|
||||
{contact.name}
|
||||
</span>
|
||||
{contact.type === "group" && (
|
||||
<TeamOutlined className={styles.groupIcon} />
|
||||
)}
|
||||
</div>
|
||||
</Checkbox>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Empty
|
||||
description="暂无联系人"
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧已选择列表 */}
|
||||
<div className={styles.selectedList}>
|
||||
<div className={styles.listHeader}>
|
||||
<span>已选择 {selectedContacts.length} 个联系人</span>
|
||||
</div>
|
||||
<div className={styles.listContent}>
|
||||
{selectedContacts.length > 0 ? (
|
||||
selectedContacts.map(contact => (
|
||||
<div key={contact.id} className={styles.selectedItem}>
|
||||
<div className={styles.contactInfo}>
|
||||
<Avatar
|
||||
size={32}
|
||||
src={contact.avatar}
|
||||
icon={
|
||||
contact.type === "group" ? (
|
||||
<TeamOutlined />
|
||||
) : (
|
||||
<UserOutlined />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className={styles.contactName}>{contact.name}</span>
|
||||
</div>
|
||||
<CloseOutlined
|
||||
className={styles.removeIcon}
|
||||
onClick={() => handleRemoveSelected(contact.id)}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Empty
|
||||
description="请选择联系人"
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransmitModal;
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default } from "./TransmitModal";
|
||||
export type { TransmitModalProps, Contact } from "./TransmitModal";
|
||||
@@ -0,0 +1,315 @@
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import {
|
||||
Modal,
|
||||
Input,
|
||||
Button,
|
||||
Avatar,
|
||||
Checkbox,
|
||||
Empty,
|
||||
Spin,
|
||||
message,
|
||||
} from "antd";
|
||||
import {
|
||||
SearchOutlined,
|
||||
CloseOutlined,
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import styles from "./TransmitModal.module.scss";
|
||||
import { weChatGroupService, contractService } from "@/utils/db";
|
||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
||||
import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
||||
|
||||
export interface Contact {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
type: "user" | "group";
|
||||
pinyin?: string; // 用于搜索
|
||||
originalData?: ContractData | weChatGroup; // 保存原始数据
|
||||
}
|
||||
|
||||
export interface TransmitModalProps {
|
||||
onConfirm?: (selectedTransmitContact: ContractData[] | weChatGroup[]) => void; // 可选,因为会自动更新到store
|
||||
}
|
||||
|
||||
const TransmitModal: React.FC<TransmitModalProps> = ({ onConfirm }) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [allContacts, setAllContacts] = useState<Contact[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const selectedTransmitContact = useWeChatStore(
|
||||
state => state.selectedTransmitContact,
|
||||
);
|
||||
// 从 Zustand store 获取更新方法
|
||||
const { updateSelectedTransmitContact } = useWeChatStore();
|
||||
const openTransmitModal = useWeChatStore(state => state.openTransmitModal);
|
||||
const updateTransmitModal = useWeChatStore(
|
||||
state => state.updateTransmitModal,
|
||||
);
|
||||
// 加载联系人数据
|
||||
const loadContacts = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
// 并行加载联系人和群组数据
|
||||
const [contractsData, groupsData] = await Promise.all([
|
||||
contractService.findAll(),
|
||||
weChatGroupService.findAll(),
|
||||
]);
|
||||
|
||||
// 转换联系人数据格式
|
||||
const contacts: Contact[] = contractsData.map(
|
||||
(contract: ContractData) => ({
|
||||
id: contract.id.toString(),
|
||||
name:
|
||||
contract.nickname ||
|
||||
contract.alias ||
|
||||
contract.wechatId ||
|
||||
"未知联系人",
|
||||
avatar: contract.avatar,
|
||||
type: "user" as const,
|
||||
pinyin: contract.quanPin,
|
||||
originalData: contract,
|
||||
}),
|
||||
);
|
||||
|
||||
// 转换群组数据格式
|
||||
const groups: Contact[] = groupsData.map((group: weChatGroup) => ({
|
||||
id: group.id.toString(),
|
||||
name: group.nickname || group.conRemark || "未知群组",
|
||||
avatar: group.chatroomAvatar,
|
||||
type: "group" as const,
|
||||
pinyin: group.nickname, // 群组可能没有拼音,使用昵称
|
||||
originalData: group,
|
||||
}));
|
||||
|
||||
// 合并并排序(联系人在前,群组在后)
|
||||
const allContactsData = [...contacts, ...groups];
|
||||
setAllContacts(allContactsData);
|
||||
} catch (err) {
|
||||
console.error("加载联系人数据失败:", err);
|
||||
setError("加载联系人数据失败,请重试");
|
||||
message.error("加载联系人数据失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 重置状态
|
||||
useEffect(() => {
|
||||
if (openTransmitModal) {
|
||||
setSearchValue("");
|
||||
updateSelectedTransmitContact([]);
|
||||
setError("");
|
||||
loadContacts();
|
||||
}
|
||||
}, [openTransmitModal]);
|
||||
|
||||
// 过滤联系人 - 支持名称和拼音搜索
|
||||
const filteredContacts = useMemo(() => {
|
||||
if (!searchValue.trim()) return allContacts;
|
||||
|
||||
const keyword = searchValue.toLowerCase();
|
||||
return allContacts.filter(
|
||||
contact =>
|
||||
contact.name.toLowerCase().includes(keyword) ||
|
||||
contact.pinyin?.toLowerCase().includes(keyword) ||
|
||||
contact.id.toLowerCase().includes(keyword),
|
||||
);
|
||||
}, [allContacts, searchValue]);
|
||||
|
||||
// 处理联系人选择
|
||||
const handleContactSelect = (contact: Contact, checked: boolean) => {
|
||||
console.log(contact);
|
||||
console.log(checked);
|
||||
};
|
||||
|
||||
// 移除已选择的联系人
|
||||
const handleRemoveSelected = (contactId: string) => {
|
||||
// updateSelectedTransmitContact(prev =>
|
||||
// selectedTransmitContact.filter(item => item.id !== contactId),
|
||||
// );
|
||||
};
|
||||
|
||||
// 确认转发
|
||||
const handleConfirm = () => {
|
||||
if (selectedTransmitContact.length === 0) {
|
||||
message.warning("请至少选择一个联系人");
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新到 Zustand store
|
||||
const originalDataList = selectedTransmitContact
|
||||
.map(contact => contact.originalData)
|
||||
.filter(Boolean) as (ContractData | weChatGroup)[];
|
||||
|
||||
// updateSelectedTransmitContact(originalDataList);
|
||||
|
||||
// 如果有外部回调,也调用它
|
||||
if (onConfirm) {
|
||||
onConfirm(selectedTransmitContact);
|
||||
}
|
||||
|
||||
message.success(`已选择${selectedTransmitContact.length}个联系人`);
|
||||
};
|
||||
|
||||
// 检查联系人是否已选择
|
||||
const isContactSelected = (contactId: string) => {
|
||||
console.log(contactId);
|
||||
|
||||
return selectedTransmitContact.some(contact => contact.id === contactId);
|
||||
};
|
||||
|
||||
// 重试加载
|
||||
const handleRetry = () => {
|
||||
loadContacts();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="转发消息"
|
||||
open={openTransmitModal}
|
||||
onCancel={() => updateTransmitModal(false)}
|
||||
width={800}
|
||||
className={styles.transmitModal}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={() => updateTransmitModal(false)}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button
|
||||
key="confirm"
|
||||
type="primary"
|
||||
onClick={handleConfirm}
|
||||
disabled={selectedTransmitContact.length === 0}
|
||||
loading={loading}
|
||||
>
|
||||
确定
|
||||
{selectedTransmitContact.length > 0 &&
|
||||
` (${selectedTransmitContact.length})`}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<div className={styles.modalContent}>
|
||||
{/* 搜索框 */}
|
||||
<div className={styles.searchContainer}>
|
||||
<Input
|
||||
placeholder="输入联系人或群名"
|
||||
prefix={<SearchOutlined />}
|
||||
value={searchValue}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
className={styles.searchInput}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.contentBody}>
|
||||
{/* 左侧联系人列表 */}
|
||||
<div className={styles.contactList}>
|
||||
<div className={styles.listHeader}>
|
||||
<span>联系人 ({filteredContacts.length})</span>
|
||||
</div>
|
||||
<div className={styles.listContent}>
|
||||
{loading ? (
|
||||
<div className={styles.loadingContainer}>
|
||||
<Spin size="large" />
|
||||
<span>加载联系人中...</span>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className={styles.errorContainer}>
|
||||
<span>{error}</span>
|
||||
<Button size="small" onClick={handleRetry}>
|
||||
重试
|
||||
</Button>
|
||||
</div>
|
||||
) : filteredContacts.length > 0 ? (
|
||||
filteredContacts.map(contact => (
|
||||
<div key={contact.id} className={styles.contactItem}>
|
||||
<Checkbox
|
||||
checked={isContactSelected(contact.id)}
|
||||
onChange={e =>
|
||||
handleContactSelect(contact, e.target.checked)
|
||||
}
|
||||
disabled={!isContactSelected(contact.id)}
|
||||
>
|
||||
<div className={styles.contactInfo}>
|
||||
<Avatar
|
||||
size={32}
|
||||
src={contact.avatar}
|
||||
icon={
|
||||
contact.type === "group" ? (
|
||||
<TeamOutlined />
|
||||
) : (
|
||||
<UserOutlined />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className={styles.contactName}>
|
||||
{contact.name}
|
||||
</span>
|
||||
{contact.type === "group" && (
|
||||
<TeamOutlined className={styles.groupIcon} />
|
||||
)}
|
||||
</div>
|
||||
</Checkbox>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Empty
|
||||
description={
|
||||
searchValue ? "未找到匹配的联系人" : "暂无联系人"
|
||||
}
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧已选择列表 */}
|
||||
<div className={styles.selectedList}>
|
||||
<div className={styles.listHeader}>
|
||||
<span>已选择 {selectedTransmitContact.length} 个联系人</span>
|
||||
</div>
|
||||
<div className={styles.listContent}>
|
||||
{selectedTransmitContact.length > 0 ? (
|
||||
selectedTransmitContact.map(contact => (
|
||||
<div key={contact.id} className={styles.selectedItem}>
|
||||
<div className={styles.contactInfo}>
|
||||
<Avatar
|
||||
size={32}
|
||||
src={contact.avatar}
|
||||
icon={
|
||||
contact.type === "group" ? (
|
||||
<TeamOutlined />
|
||||
) : (
|
||||
<UserOutlined />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className={styles.contactName}>{contact.name}</span>
|
||||
{contact.type === "group" && (
|
||||
<TeamOutlined className={styles.groupIcon} />
|
||||
)}
|
||||
</div>
|
||||
<CloseOutlined
|
||||
className={styles.removeIcon}
|
||||
onClick={() => handleRemoveSelected(contact.id)}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Empty
|
||||
description="请选择联系人"
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransmitModal;
|
||||
@@ -12,6 +12,8 @@ import styles from "./MessageRecord.module.scss";
|
||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
||||
import { fetchReCallApi } from "./api";
|
||||
import TransmitModal from "./components/TransmitModal";
|
||||
|
||||
interface MessageRecordProps {
|
||||
contract: ContractData | weChatGroup;
|
||||
}
|
||||
@@ -44,6 +46,11 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
state.kfUserList.find(kf => kf.id === contract.wechatAccountId),
|
||||
);
|
||||
|
||||
const openTransmitModal = useWeChatStore(state => state.openTransmitModal);
|
||||
const updateTransmitModal = useWeChatStore(
|
||||
state => state.updateTransmitModal,
|
||||
);
|
||||
|
||||
// 判断是否为表情包URL的工具函数
|
||||
const isEmojiUrl = (content: string): boolean => {
|
||||
return (
|
||||
@@ -662,6 +669,7 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
};
|
||||
|
||||
const handleForwardMessage = (messageData: ChatRecord) => {
|
||||
updateTransmitModal(true);
|
||||
// 转发消息的处理逻辑
|
||||
console.log("转发消息:", messageData);
|
||||
};
|
||||
@@ -702,6 +710,13 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmTransmit = (
|
||||
selectedContacts: ContractData[] | weChatGroup[],
|
||||
) => {
|
||||
// 确认转发逻辑
|
||||
console.log("确认转发:", selectedContacts);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.messagesContainer}>
|
||||
<div className={styles.loadMore} onClick={() => loadMoreMessages()}>
|
||||
@@ -727,7 +742,6 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
</React.Fragment>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
|
||||
{/* 右键菜单组件 */}
|
||||
<ClickMenu
|
||||
visible={contextMenu.visible}
|
||||
@@ -738,6 +752,12 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
|
||||
onClose={handleCloseContextMenu}
|
||||
onCommad={handCommad}
|
||||
/>
|
||||
{/* 转发模态框 */}
|
||||
<TransmitModal
|
||||
onConfirm={(selectedContacts: ContractData[] | weChatGroup[]) =>
|
||||
handleConfirmTransmit(selectedContacts)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,64 +4,104 @@ import {
|
||||
likeListItem,
|
||||
FriendsCircleItem,
|
||||
} from "@/pages/pc/ckbox/weChat/components/SidebarMenu/FriendsCicle/index.data";
|
||||
// 微信聊天相关的类型定义
|
||||
export interface WeChatState {
|
||||
//选择聊天记录
|
||||
selectedMessage: ChatRecord[];
|
||||
updateSelectedMessage: (message: ChatRecord[]) => void;
|
||||
//选择用户或群
|
||||
selectedContact: ContractData[] | weChatGroup[];
|
||||
updateSelectedContact: (contact: ContractData[] | weChatGroup[]) => void;
|
||||
|
||||
/**
|
||||
* 微信聊天状态管理接口定义
|
||||
* 包含聊天消息、联系人管理、朋友圈等功能的状态和方法
|
||||
*/
|
||||
export interface WeChatState {
|
||||
// ==================== Transmit Module =========Start===========
|
||||
/** 选中的聊天记录列表 */
|
||||
selectedChatRecords: ChatRecord[];
|
||||
/** 更新选中的聊天记录 */
|
||||
updateSelectedChatRecords: (message: ChatRecord[]) => void;
|
||||
|
||||
/** 选中的联系人或群组列表 */
|
||||
selectedTransmitContact: ContractData[] | weChatGroup[];
|
||||
/** 更新选中的联系人或群组 */
|
||||
updateSelectedTransmitContact: (
|
||||
contact: ContractData[] | weChatGroup[],
|
||||
) => void;
|
||||
|
||||
/** 转发弹窗开启状态 */
|
||||
openTransmitModal: boolean;
|
||||
/** 更新转发弹窗状态 */
|
||||
updateTransmitModal: (open: boolean) => void;
|
||||
// 当前选中的联系人/群组
|
||||
// ==================== Transmit Module =========END===========
|
||||
|
||||
// ==================== 当前联系人管理 ====================
|
||||
/** 当前选中的联系人/群组 */
|
||||
currentContract: ContractData | weChatGroup | null;
|
||||
// CurrentContact 相关方法
|
||||
/** 清空当前联系人 */
|
||||
clearCurrentContact: () => void;
|
||||
/** 设置当前联系人 */
|
||||
setCurrentContact: (
|
||||
contract: ContractData | weChatGroup,
|
||||
isExist?: boolean,
|
||||
) => void;
|
||||
|
||||
// 当前聊天用户的消息列表(只存储当前聊天用户的消息)
|
||||
// ==================== 聊天消息管理 ====================
|
||||
/** 当前聊天的消息列表 */
|
||||
currentMessages: ChatRecord[];
|
||||
// 添加消息
|
||||
/** 添加新消息 */
|
||||
addMessage: (message: ChatRecord) => void;
|
||||
// 替换消息
|
||||
/** 更新指定消息 */
|
||||
updateMessage: (messageId: number, updates: Partial<ChatRecord>) => void;
|
||||
// 撤回消息
|
||||
/** 撤回指定消息 */
|
||||
recallMessage: (messageId: number) => void;
|
||||
// 消息加载状态
|
||||
/** 消息加载状态 */
|
||||
messagesLoading: boolean;
|
||||
/** 数据初始化加载状态 */
|
||||
isLoadingData: boolean;
|
||||
/** 当前群组成员列表 */
|
||||
currentGroupMembers: any[];
|
||||
showCheckbox: boolean;
|
||||
updateShowCheckbox: (show: boolean) => void;
|
||||
EnterModule: string;
|
||||
// EnterModule 相关方法
|
||||
updateEnterModule: (module: string) => void;
|
||||
MomentCommon: FriendsCircleItem[];
|
||||
// MomentCommon 相关方法
|
||||
clearMomentCommon: () => void;
|
||||
addMomentCommon: (moment: FriendsCircleItem[]) => void;
|
||||
updateMomentCommon: (moments: FriendsCircleItem[]) => void;
|
||||
|
||||
// ==================== 界面状态管理 ====================
|
||||
/** 是否显示复选框 */
|
||||
showCheckbox: boolean;
|
||||
/** 更新复选框显示状态 */
|
||||
updateShowCheckbox: (show: boolean) => void;
|
||||
/** 进入模块类型 (common | multipleForwarding) */
|
||||
EnterModule: string;
|
||||
/** 更新进入模块类型 */
|
||||
updateEnterModule: (module: string) => void;
|
||||
|
||||
// ==================== 朋友圈相关 ====================
|
||||
/** 朋友圈数据列表 */
|
||||
MomentCommon: FriendsCircleItem[];
|
||||
/** 朋友圈数据加载状态 */
|
||||
MomentCommonLoading: boolean;
|
||||
// MomentCommon 相关方法
|
||||
/** 清空朋友圈数据 */
|
||||
clearMomentCommon: () => void;
|
||||
/** 添加朋友圈数据 */
|
||||
addMomentCommon: (moment: FriendsCircleItem[]) => void;
|
||||
/** 更新朋友圈数据 */
|
||||
updateMomentCommon: (moments: FriendsCircleItem[]) => void;
|
||||
/** 更新朋友圈加载状态 */
|
||||
updateMomentCommonLoading: (loading: boolean) => void;
|
||||
/** 更新朋友圈点赞 */
|
||||
updateLikeMoment: (snsId: string, likeList: likeListItem[]) => void;
|
||||
/** 更新朋友圈评论 */
|
||||
updateComment: (snsId: string, commentList: CommentItem[]) => void;
|
||||
|
||||
// ==================== 消息加载方法 ====================
|
||||
/** 加载聊天消息 */
|
||||
loadChatMessages: (Init: boolean, To?: number) => Promise<void>;
|
||||
/** 搜索消息 */
|
||||
SearchMessage: (params: {
|
||||
From: number;
|
||||
To: number;
|
||||
keyword: string;
|
||||
Count?: number;
|
||||
}) => Promise<void>;
|
||||
// 视频消息处理方法
|
||||
|
||||
// ==================== 视频消息处理 ====================
|
||||
/** 设置视频消息加载状态 */
|
||||
setVideoLoading: (messageId: number, isLoading: boolean) => void;
|
||||
/** 设置视频消息URL */
|
||||
setVideoUrl: (messageId: number, videoUrl: string) => void;
|
||||
|
||||
// ==================== 消息接收处理 ====================
|
||||
/** 接收新消息处理 */
|
||||
receivedMsg: (message: ChatRecord) => void;
|
||||
}
|
||||
|
||||
@@ -20,36 +20,52 @@ import {
|
||||
useCkChatStore,
|
||||
} from "@/store/module/ckchat/ckchat";
|
||||
|
||||
/**
|
||||
* 微信聊天状态管理 Store
|
||||
* 使用 Zustand 管理微信聊天相关的状态和操作
|
||||
*/
|
||||
export const useWeChatStore = create<WeChatState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
//选择聊天记录
|
||||
selectedMessage: [],
|
||||
updateSelectedMessage: (message: ChatRecord[]) => {
|
||||
set({ selectedMessage: message });
|
||||
// ==================== Transmit Module =========Start===========
|
||||
/** 选中的聊天记录列表 */
|
||||
selectedChatRecords: [],
|
||||
/** 更新选中的聊天记录 */
|
||||
updateSelectedChatRecords: (message: ChatRecord[]) => {
|
||||
set({ selectedChatRecords: message });
|
||||
},
|
||||
//选择用户或群
|
||||
selectedContact: [],
|
||||
updateSelectedContact: (contact: ContractData[] | weChatGroup[]) => {
|
||||
set({ selectedContact: contact });
|
||||
},
|
||||
//打开转发弹窗
|
||||
openTransmitModal: false,
|
||||
|
||||
/** 选中的联系人或群组列表 */
|
||||
selectedTransmitContact: [],
|
||||
/** 更新选中的联系人或群组 */
|
||||
updateSelectedTransmitContact: (
|
||||
contact: ContractData[] | weChatGroup[],
|
||||
) => {
|
||||
set({ selectedTransmitContact: contact });
|
||||
},
|
||||
|
||||
/** 转发弹窗开启状态 */
|
||||
openTransmitModal: false,
|
||||
/** 更新转发弹窗状态 */
|
||||
updateTransmitModal: (open: boolean) => {
|
||||
set({ openTransmitModal: open });
|
||||
},
|
||||
// ==================== Transmit Module =========END===========
|
||||
|
||||
// 初始状态
|
||||
// ==================== 当前联系人管理状态 ====================
|
||||
/** 当前选中的联系人/群组 */
|
||||
currentContract: null,
|
||||
/** 当前聊天的消息列表 */
|
||||
currentMessages: [],
|
||||
//添加消息
|
||||
|
||||
// ==================== 聊天消息管理方法 ====================
|
||||
/** 添加新消息到当前聊天 */
|
||||
addMessage: message => {
|
||||
set(state => ({
|
||||
currentMessages: [...state.currentMessages, message],
|
||||
}));
|
||||
},
|
||||
//替换消息
|
||||
/** 更新指定消息内容 */
|
||||
updateMessage: (messageId, updates) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg =>
|
||||
@@ -57,7 +73,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
),
|
||||
}));
|
||||
},
|
||||
//撤回消息
|
||||
/** 撤回指定消息 */
|
||||
recallMessage: (messageId: number) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.filter(
|
||||
@@ -66,31 +82,44 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}));
|
||||
},
|
||||
|
||||
// ==================== 加载状态管理 ====================
|
||||
/** 消息加载状态 */
|
||||
messagesLoading: false,
|
||||
/** 数据初始化加载状态 */
|
||||
isLoadingData: false,
|
||||
/** 当前群组成员列表 */
|
||||
currentGroupMembers: [],
|
||||
|
||||
// ==================== 界面状态管理 ====================
|
||||
/** 是否显示复选框 */
|
||||
showCheckbox: false,
|
||||
/** 更新复选框显示状态 */
|
||||
updateShowCheckbox: (show: boolean) => {
|
||||
set({ showCheckbox: show });
|
||||
},
|
||||
EnterModule: "common", //common | multipleForwarding
|
||||
/** 进入模块类型 (common | multipleForwarding) */
|
||||
EnterModule: "common",
|
||||
/** 更新进入模块类型 */
|
||||
updateEnterModule: (module: string) => {
|
||||
set({ EnterModule: module });
|
||||
},
|
||||
MomentCommon: [], //朋友圈数据
|
||||
MomentCommonLoading: false, //朋友圈数据是否正在加载
|
||||
|
||||
// MomentCommon 相关方法
|
||||
// ==================== 朋友圈相关状态 ====================
|
||||
/** 朋友圈数据列表 */
|
||||
MomentCommon: [],
|
||||
/** 朋友圈数据加载状态 */
|
||||
MomentCommonLoading: false,
|
||||
/** 更新朋友圈加载状态 */
|
||||
updateMomentCommonLoading: (loading: boolean) => {
|
||||
set({ MomentCommonLoading: loading });
|
||||
},
|
||||
//============方法列表============
|
||||
|
||||
//清空当前联系人
|
||||
// ==================== 当前联系人管理方法 ====================
|
||||
/** 清空当前联系人和消息 */
|
||||
clearCurrentContact: () => {
|
||||
set({ currentContract: null, currentMessages: [] });
|
||||
},
|
||||
// Actions
|
||||
/** 设置当前联系人并加载相关数据 */
|
||||
setCurrentContact: (
|
||||
contract: ContractData | weChatGroup,
|
||||
isExist?: boolean,
|
||||
@@ -112,6 +141,9 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
state.loadChatMessages(true, 4704624000000);
|
||||
});
|
||||
},
|
||||
|
||||
// ==================== 消息加载方法 ====================
|
||||
/** 加载聊天消息 */
|
||||
loadChatMessages: async (Init: boolean, To?: number) => {
|
||||
const state = useWeChatStore.getState();
|
||||
const contact = state.currentContract;
|
||||
@@ -127,6 +159,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
};
|
||||
|
||||
if ("chatroomId" in contact && contact.chatroomId) {
|
||||
// 群聊消息加载
|
||||
params.wechatChatroomId = contact.id;
|
||||
const messages = await getChatroomMessages(params);
|
||||
const currentGroupMembers = await getGroupMembers({
|
||||
@@ -143,6 +176,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 私聊消息加载
|
||||
params.wechatFriendId = contact.id;
|
||||
const messages = await getChatMessages(params);
|
||||
if (Init) {
|
||||
@@ -163,6 +197,8 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
set({ messagesLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
/** 搜索消息 */
|
||||
SearchMessage: async ({
|
||||
From = 1,
|
||||
To = 4704624000000,
|
||||
@@ -189,6 +225,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
};
|
||||
|
||||
if ("chatroomId" in contact && contact.chatroomId) {
|
||||
// 群聊消息搜索
|
||||
params.wechatChatroomId = contact.id;
|
||||
const messages = await getChatroomMessages(params);
|
||||
const currentGroupMembers = await getGroupMembers({
|
||||
@@ -196,6 +233,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
});
|
||||
set({ currentMessages: messages || [], currentGroupMembers });
|
||||
} else {
|
||||
// 私聊消息搜索
|
||||
params.wechatFriendId = contact.id;
|
||||
const messages = await getChatMessages(params);
|
||||
set({ currentMessages: messages || [] });
|
||||
@@ -208,29 +246,34 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}
|
||||
},
|
||||
|
||||
/** 设置消息加载状态 */
|
||||
setMessageLoading: loading => {
|
||||
set({ messagesLoading: Boolean(loading) });
|
||||
},
|
||||
|
||||
// ==================== 消息接收处理 ====================
|
||||
/** 接收新消息处理 */
|
||||
receivedMsg: async message => {
|
||||
const currentContract = useWeChatStore.getState().currentContract;
|
||||
//判断群还是好友
|
||||
// 判断是群聊还是私聊
|
||||
const getMessageId =
|
||||
message?.wechatChatroomId || message.wechatFriendId;
|
||||
const isWechatGroup = message?.wechatChatroomId;
|
||||
//当前选中聊天的群或好友
|
||||
|
||||
// 如果是当前选中的聊天,直接添加到消息列表
|
||||
if (currentContract && currentContract.id == getMessageId) {
|
||||
set(state => ({
|
||||
currentMessages: [...state.currentMessages, message],
|
||||
}));
|
||||
} else {
|
||||
//更新消息列表unread数值,根据接收的++1 这样
|
||||
// 更新其他聊天的未读消息数
|
||||
const chatSessions = useCkChatStore.getState().chatSessions;
|
||||
const session = chatSessions.find(item => item.id == getMessageId);
|
||||
if (session) {
|
||||
session.unreadCount = Number(session.unreadCount) + 1;
|
||||
updateChatSession(session);
|
||||
} else {
|
||||
// 如果会话不存在,创建新会话
|
||||
if (isWechatGroup) {
|
||||
const [group] = await weChatGroupService.findByIds(getMessageId);
|
||||
if (group) {
|
||||
@@ -250,12 +293,16 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}
|
||||
},
|
||||
|
||||
// 便捷选择器
|
||||
// ==================== 便捷选择器方法 ====================
|
||||
/** 获取当前联系人 */
|
||||
getCurrentContact: () => get().currentContract,
|
||||
/** 获取当前消息列表 */
|
||||
getCurrentMessages: () => get().currentMessages,
|
||||
/** 获取消息加载状态 */
|
||||
getMessagesLoading: () => get().messagesLoading,
|
||||
|
||||
// 视频消息处理方法
|
||||
// ==================== 视频消息处理方法 ====================
|
||||
/** 设置视频消息加载状态 */
|
||||
setVideoLoading: (messageId: number, isLoading: boolean) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg => {
|
||||
@@ -278,6 +325,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}));
|
||||
},
|
||||
|
||||
/** 设置视频消息URL */
|
||||
setVideoUrl: (messageId: number, videoUrl: string) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg => {
|
||||
@@ -309,6 +357,9 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}),
|
||||
}));
|
||||
},
|
||||
|
||||
// ==================== 数据清理方法 ====================
|
||||
/** 清空所有数据 */
|
||||
clearAllData: () => {
|
||||
set({
|
||||
currentContract: null,
|
||||
@@ -317,21 +368,25 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
});
|
||||
},
|
||||
|
||||
// MomentCommon 相关方法
|
||||
// ==================== 朋友圈管理方法 ====================
|
||||
/** 清空朋友圈数据 */
|
||||
clearMomentCommon: () => {
|
||||
set({ MomentCommon: [] });
|
||||
},
|
||||
|
||||
/** 添加朋友圈数据 */
|
||||
addMomentCommon: moment => {
|
||||
set(state => ({
|
||||
MomentCommon: [...state.MomentCommon, ...moment],
|
||||
}));
|
||||
},
|
||||
|
||||
/** 更新朋友圈数据 */
|
||||
updateMomentCommon: moments => {
|
||||
set({ MomentCommon: moments });
|
||||
},
|
||||
|
||||
/** 更新朋友圈点赞 */
|
||||
updateLikeMoment: (snsId: string, likeList: likeListItem[]) => {
|
||||
set(state => ({
|
||||
MomentCommon: state.MomentCommon.map(moment =>
|
||||
@@ -340,6 +395,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}));
|
||||
},
|
||||
|
||||
/** 更新朋友圈评论 */
|
||||
updateComment: (snsId: string, commentList: CommentItem[]) => {
|
||||
set(state => ({
|
||||
MomentCommon: state.MomentCommon.map(moment =>
|
||||
@@ -357,12 +413,16 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
),
|
||||
);
|
||||
|
||||
// 导出便捷的选择器函数
|
||||
// ==================== 便捷选择器导出 ====================
|
||||
/** 获取当前联系人的 Hook */
|
||||
export const useCurrentContact = () =>
|
||||
useWeChatStore(state => state.currentContract);
|
||||
/** 获取当前消息列表的 Hook */
|
||||
export const useCurrentMessages = () =>
|
||||
useWeChatStore(state => state.currentMessages);
|
||||
/** 获取消息加载状态的 Hook */
|
||||
export const useMessagesLoading = () =>
|
||||
useWeChatStore(state => state.messagesLoading);
|
||||
/** 获取复选框显示状态的 Hook */
|
||||
export const useShowCheckbox = () =>
|
||||
useWeChatStore(state => state.showCheckbox);
|
||||
|
||||
Reference in New Issue
Block a user