feat(数据库): 添加消息列表数据接口和表
refactor(聊天会话): 根据选中客服过滤会话列表 fix(聊天窗口): 移除重复的未读消息清除逻辑 refactor(侧边栏): 统一联系人点击处理逻辑 chore: 删除无用的数据库测试文件
This commit is contained in:
@@ -36,7 +36,7 @@ import {
|
|||||||
StarOutlined,
|
StarOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
||||||
import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api";
|
import { getMessages } from "@/pages/pc/ckbox/api";
|
||||||
import styles from "./ChatWindow.module.scss";
|
import styles from "./ChatWindow.module.scss";
|
||||||
import { useWebSocketStore, WebSocketMessage } from "@/store/module/websocket";
|
import { useWebSocketStore, WebSocketMessage } from "@/store/module/websocket";
|
||||||
import { formatWechatTime } from "@/utils/common";
|
import { formatWechatTime } from "@/utils/common";
|
||||||
@@ -68,30 +68,27 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
|||||||
>({});
|
>({});
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const getKfSelectedUser = useCkChatStore(state => state.getKfSelectedUser());
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearUnreadCount([contract.id]).then(() => {
|
setLoading(true);
|
||||||
setLoading(true);
|
const params: any = {
|
||||||
const params: any = {
|
wechatAccountId: contract.wechatAccountId,
|
||||||
wechatAccountId: contract.wechatAccountId,
|
From: 1,
|
||||||
From: 1,
|
To: +new Date() + 1000,
|
||||||
To: +new Date() + 1000,
|
Count: 100,
|
||||||
Count: 100,
|
olderData: true,
|
||||||
olderData: true,
|
};
|
||||||
};
|
if (contract.groupId == 1) {
|
||||||
if (contract.groupId == 1) {
|
params.wechatFriendId = contract.id;
|
||||||
params.wechatFriendId = contract.id;
|
} else {
|
||||||
} else {
|
params.wechatChatroomId = contract.id;
|
||||||
params.wechatChatroomId = contract.id;
|
}
|
||||||
}
|
getMessages(params)
|
||||||
getMessages(params)
|
.then(msg => {
|
||||||
.then(msg => {
|
setMessages(msg);
|
||||||
setMessages(msg);
|
})
|
||||||
})
|
.finally(() => {
|
||||||
.finally(() => {
|
setLoading(false);
|
||||||
setLoading(false);
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
}, [contract.id]);
|
}, [contract.id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import { formatWechatTime } from "@/utils/common";
|
|||||||
interface MessageListProps {
|
interface MessageListProps {
|
||||||
chatSessions: ContractData[] | weChatGroup[];
|
chatSessions: ContractData[] | weChatGroup[];
|
||||||
currentChat: ContractData | weChatGroup;
|
currentChat: ContractData | weChatGroup;
|
||||||
onChatSelect: (chat: ContractData | weChatGroup) => void;
|
onContactClick: (chat: ContractData | weChatGroup) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageList: React.FC<MessageListProps> = ({
|
const MessageList: React.FC<MessageListProps> = ({
|
||||||
chatSessions,
|
chatSessions,
|
||||||
currentChat,
|
currentChat,
|
||||||
onChatSelect,
|
onContactClick,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.messageList}>
|
<div className={styles.messageList}>
|
||||||
@@ -25,7 +25,7 @@ const MessageList: React.FC<MessageListProps> = ({
|
|||||||
className={`${styles.messageItem} ${
|
className={`${styles.messageItem} ${
|
||||||
currentChat?.id === session.id ? styles.active : ""
|
currentChat?.id === session.id ? styles.active : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onChatSelect(session)}
|
onClick={() => onContactClick(session)}
|
||||||
>
|
>
|
||||||
<div className={styles.messageInfo}>
|
<div className={styles.messageInfo}>
|
||||||
<Badge count={session.unreadCount} size="small">
|
<Badge count={session.unreadCount} size="small">
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
|||||||
import WechatFriends from "./WechatFriends";
|
import WechatFriends from "./WechatFriends";
|
||||||
import MessageList from "./MessageList/index";
|
import MessageList from "./MessageList/index";
|
||||||
import styles from "./SidebarMenu.module.scss";
|
import styles from "./SidebarMenu.module.scss";
|
||||||
import { getChatSessions } from "@/store/module/ckchat/ckchat";
|
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
||||||
|
|
||||||
interface SidebarMenuProps {
|
interface SidebarMenuProps {
|
||||||
contracts: ContractData[] | weChatGroup[];
|
contracts: ContractData[] | weChatGroup[];
|
||||||
currentChat: ContractData | weChatGroup;
|
currentChat: ContractData | weChatGroup;
|
||||||
onContactClick: (contract: ContractData | weChatGroup) => void;
|
onContactClick: (contract: ContractData | weChatGroup) => void;
|
||||||
onChatSelect: (chat: ContractData | weChatGroup) => void;
|
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,10 +23,9 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
|||||||
contracts,
|
contracts,
|
||||||
currentChat,
|
currentChat,
|
||||||
onContactClick,
|
onContactClick,
|
||||||
onChatSelect,
|
|
||||||
loading = false,
|
loading = false,
|
||||||
}) => {
|
}) => {
|
||||||
const chatSessions = getChatSessions();
|
const chatSessions = useCkChatStore(state => state.getChatSessions());
|
||||||
|
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const [activeTab, setActiveTab] = useState("chats");
|
const [activeTab, setActiveTab] = useState("chats");
|
||||||
@@ -145,7 +143,7 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
|||||||
return (
|
return (
|
||||||
<MessageList
|
<MessageList
|
||||||
chatSessions={getFilteredSessions()}
|
chatSessions={getFilteredSessions()}
|
||||||
onChatSelect={onChatSelect}
|
onContactClick={onContactClick}
|
||||||
currentChat={currentChat}
|
currentChat={currentChat}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,22 +16,6 @@ const VerticalUserList: React.FC = () => {
|
|||||||
};
|
};
|
||||||
const kfUserList = useCkChatStore(state => state.kfUserList);
|
const kfUserList = useCkChatStore(state => state.kfUserList);
|
||||||
const kfSelected = useCkChatStore(state => state.kfSelected);
|
const kfSelected = useCkChatStore(state => state.kfSelected);
|
||||||
const [kefuList, setKefuList] = useState([]);
|
|
||||||
|
|
||||||
// // 获取客服列表数据
|
|
||||||
// useEffect(() => {
|
|
||||||
// const fetchKfUserList = async () => {
|
|
||||||
// try {
|
|
||||||
// const data = await getkfUserList();
|
|
||||||
// setKefuList(data || []);
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("获取客服列表失败:", error);
|
|
||||||
// setKefuList([]);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// fetchKfUserList();
|
|
||||||
// }, [getkfUserList]);
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.verticalUserList}>
|
<div className={styles.verticalUserList}>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import styles from "./index.module.scss";
|
|||||||
import { addChatSession } from "@/store/module/ckchat/ckchat";
|
import { addChatSession } from "@/store/module/ckchat/ckchat";
|
||||||
const { Header, Content, Sider } = Layout;
|
const { Header, Content, Sider } = Layout;
|
||||||
import { chatInitAPIdata } from "./main";
|
import { chatInitAPIdata } from "./main";
|
||||||
|
import { clearUnreadCount } from "@/pages/pc/ckbox/api";
|
||||||
import {
|
import {
|
||||||
KfUserListData,
|
KfUserListData,
|
||||||
weChatGroup,
|
weChatGroup,
|
||||||
@@ -57,9 +58,13 @@ const CkboxPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
//开始开启聊天
|
||||||
const handleContactClick = (contract: ContractData | weChatGroup) => {
|
const handleContactClick = (contract: ContractData | weChatGroup) => {
|
||||||
addChatSession(contract);
|
clearUnreadCount([contract.id]).then(() => {
|
||||||
setCurrentChat(contract);
|
contract.unreadCount = 0;
|
||||||
|
addChatSession(contract);
|
||||||
|
setCurrentChat(contract);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSendMessage = async (message: string) => {
|
const handleSendMessage = async (message: string) => {
|
||||||
@@ -107,7 +112,6 @@ const CkboxPage: React.FC = () => {
|
|||||||
contracts={contracts}
|
contracts={contracts}
|
||||||
currentChat={currentChat}
|
currentChat={currentChat}
|
||||||
onContactClick={handleContactClick}
|
onContactClick={handleContactClick}
|
||||||
onChatSelect={setCurrentChat}
|
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
</Sider>
|
</Sider>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
|||||||
chatSessions: [], //聊天会话
|
chatSessions: [], //聊天会话
|
||||||
kfUserList: [], //客服列表
|
kfUserList: [], //客服列表
|
||||||
newContractList: [], //联系人分组
|
newContractList: [], //联系人分组
|
||||||
kfSelected: 0,
|
kfSelected: 0, //选中的客服
|
||||||
//客服列表
|
//客服列表
|
||||||
asyncKfUserList: async data => {
|
asyncKfUserList: async data => {
|
||||||
set({ kfUserList: data });
|
set({ kfUserList: data });
|
||||||
@@ -80,7 +80,13 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
|||||||
// 获取聊天会话
|
// 获取聊天会话
|
||||||
getChatSessions: () => {
|
getChatSessions: () => {
|
||||||
const state = useCkChatStore.getState();
|
const state = useCkChatStore.getState();
|
||||||
return state.chatSessions;
|
if (state.kfSelected != 0) {
|
||||||
|
return state.chatSessions.filter(
|
||||||
|
item => item.wechatAccountId === state.kfSelected,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return state.chatSessions;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// 添加聊天会话
|
// 添加聊天会话
|
||||||
addChatSession: (session: ContractData | weChatGroup) => {
|
addChatSession: (session: ContractData | weChatGroup) => {
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
/**
|
|
||||||
* 数据库版本升级测试脚本
|
|
||||||
* 用于验证数据库版本升级逻辑是否正常工作
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { db } from "./db";
|
|
||||||
|
|
||||||
// 重置数据库(完全删除并重新创建)
|
|
||||||
export async function resetDatabase() {
|
|
||||||
try {
|
|
||||||
console.log("开始重置数据库...");
|
|
||||||
|
|
||||||
// 关闭数据库连接
|
|
||||||
if (db.isOpen()) {
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除数据库
|
|
||||||
await db.delete();
|
|
||||||
console.log("旧数据库已删除");
|
|
||||||
|
|
||||||
// 重新打开数据库(这会创建新的数据库)
|
|
||||||
await db.open();
|
|
||||||
|
|
||||||
console.log("数据库重置成功!");
|
|
||||||
console.log("当前数据库版本:", db.verno);
|
|
||||||
console.log("数据库名称:", db.name);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
version: db.verno,
|
|
||||||
tables: db.tables.map(table => table.name),
|
|
||||||
message: "数据库重置成功",
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("数据库重置失败:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
|
||||||
message: "数据库重置失败",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试数据库初始化和版本升级
|
|
||||||
export async function testDatabaseUpgrade() {
|
|
||||||
try {
|
|
||||||
console.log("开始测试数据库初始化...");
|
|
||||||
|
|
||||||
// 首先尝试正常打开数据库
|
|
||||||
try {
|
|
||||||
await db.open();
|
|
||||||
} catch (upgradeError) {
|
|
||||||
// 如果遇到升级错误,尝试重置数据库
|
|
||||||
if (
|
|
||||||
upgradeError.message &&
|
|
||||||
upgradeError.message.includes("primary key")
|
|
||||||
) {
|
|
||||||
console.log("检测到主键冲突,尝试重置数据库...");
|
|
||||||
const resetResult = await resetDatabase();
|
|
||||||
if (!resetResult.success) {
|
|
||||||
throw new Error(`数据库重置失败: ${resetResult.error}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw upgradeError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("数据库初始化成功!");
|
|
||||||
console.log("当前数据库版本:", db.verno);
|
|
||||||
console.log("数据库名称:", db.name);
|
|
||||||
|
|
||||||
// 检查表是否存在
|
|
||||||
const tables = db.tables.map(table => table.name);
|
|
||||||
console.log("数据库表:", tables);
|
|
||||||
|
|
||||||
// 测试基本操作
|
|
||||||
const testData = {
|
|
||||||
tenantId: 1, // 修正为number类型
|
|
||||||
wechatId: "test-wechat-id",
|
|
||||||
nickname: "测试用户",
|
|
||||||
alias: "测试别名",
|
|
||||||
};
|
|
||||||
|
|
||||||
// 测试创建数据
|
|
||||||
const userId = await db.kfUsers.add({
|
|
||||||
...testData,
|
|
||||||
id: 0, // 添加必需的id字段
|
|
||||||
currentDeviceId: 0,
|
|
||||||
isDeleted: false,
|
|
||||||
deleteTime: "",
|
|
||||||
groupId: 0,
|
|
||||||
memo: "", // 备注信息
|
|
||||||
wechatVersion: "",
|
|
||||||
labels: [],
|
|
||||||
lastUpdateTime: new Date().toISOString(), // 修复语法错误,使用字符串类型
|
|
||||||
serverId: "test-server-id-001", // 提供有意义的测试值
|
|
||||||
// 移除不属于KfUserListData接口的字段
|
|
||||||
signature: "",
|
|
||||||
bindQQ: "",
|
|
||||||
bindEmail: "",
|
|
||||||
bindMobile: "",
|
|
||||||
bindWeixin: "",
|
|
||||||
bindAlipay: "",
|
|
||||||
bindTaobao: "",
|
|
||||||
bindJd: "",
|
|
||||||
bindDouyin: "",
|
|
||||||
bindKuaishou: "",
|
|
||||||
bindBilibili: "",
|
|
||||||
avatar: "",
|
|
||||||
gender: 0,
|
|
||||||
region: "",
|
|
||||||
createTime: new Date().toISOString(), // 使用字符串类型
|
|
||||||
});
|
|
||||||
console.log("创建测试用户成功,ID:", userId);
|
|
||||||
|
|
||||||
// 测试查询数据
|
|
||||||
const user = await db.kfUsers.get(userId);
|
|
||||||
console.log("查询测试用户:", user);
|
|
||||||
|
|
||||||
// 清理测试数据
|
|
||||||
await db.kfUsers.delete(userId);
|
|
||||||
console.log("清理测试数据完成");
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
version: db.verno,
|
|
||||||
tables: tables,
|
|
||||||
message: "数据库版本升级测试通过",
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("数据库测试失败:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
|
||||||
message: "数据库版本升级测试失败",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果直接运行此文件,执行测试
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
testDatabaseUpgrade().then(result => {
|
|
||||||
console.log("测试结果:", result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -57,12 +57,24 @@ export interface NewContactListData {
|
|||||||
weChatGroup: weChatGroup[];
|
weChatGroup: weChatGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 消息列表数据接口
|
||||||
|
export interface MessageListData extends Omit<ContractData, "id"> {
|
||||||
|
serverId: number | string; // 服务器ID作为主键
|
||||||
|
id?: number; // 接口数据的原始ID字段
|
||||||
|
wechatAccountId: number; // 微信账号ID
|
||||||
|
chatroomAvatar?: string; // 群头像
|
||||||
|
chatroomName?: string; // 群名称
|
||||||
|
chatroomId?: number; // 群ID
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
// 数据库类
|
// 数据库类
|
||||||
class CunkebaoDatabase extends Dexie {
|
class CunkebaoDatabase extends Dexie {
|
||||||
kfUsers!: Table<KfUserWithServerId>;
|
kfUsers!: Table<KfUserWithServerId>;
|
||||||
weChatGroup!: Table<weChatGroupServerId>;
|
weChatGroup!: Table<weChatGroupServerId>;
|
||||||
contracts!: Table<ContractWithServerId>;
|
contracts!: Table<ContractWithServerId>;
|
||||||
newContractList!: Table<NewContactListData>;
|
newContractList!: Table<NewContactListData>;
|
||||||
|
messageList!: Table<MessageListData>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("CunkebaoDatabase");
|
super("CunkebaoDatabase");
|
||||||
|
|||||||
Reference in New Issue
Block a user