diff --git a/Cunkebao/dist/.vite/manifest.json b/Cunkebao/dist/.vite/manifest.json
index 42480c71..17e97181 100644
--- a/Cunkebao/dist/.vite/manifest.json
+++ b/Cunkebao/dist/.vite/manifest.json
@@ -1,9 +1,9 @@
{
- "_charts-DDGb1us0.js": {
- "file": "assets/charts-DDGb1us0.js",
+ "_charts-M0qaf_ew.js": {
+ "file": "assets/charts-M0qaf_ew.js",
"name": "charts",
"imports": [
- "_ui-rFvxQTWo.js",
+ "_ui-D5qYGnLz.js",
"_vendor-2vc8h_ct.js"
]
},
@@ -11,8 +11,8 @@
"file": "assets/ui-D0C0OGrH.css",
"src": "_ui-D0C0OGrH.css"
},
- "_ui-rFvxQTWo.js": {
- "file": "assets/ui-rFvxQTWo.js",
+ "_ui-D5qYGnLz.js": {
+ "file": "assets/ui-D5qYGnLz.js",
"name": "ui",
"imports": [
"_vendor-2vc8h_ct.js"
@@ -33,18 +33,18 @@
"name": "vendor"
},
"index.html": {
- "file": "assets/index-C3xy08Hg.js",
+ "file": "assets/index-BQxyt58_.js",
"name": "index",
"src": "index.html",
"isEntry": true,
"imports": [
"_vendor-2vc8h_ct.js",
- "_ui-rFvxQTWo.js",
"_utils-6WF66_dS.js",
- "_charts-DDGb1us0.js"
+ "_ui-D5qYGnLz.js",
+ "_charts-M0qaf_ew.js"
],
"css": [
- "assets/index-D4Jt-UDy.css"
+ "assets/index-B6B8u-1D.css"
]
}
}
\ No newline at end of file
diff --git a/Cunkebao/dist/index.html b/Cunkebao/dist/index.html
index f4b62be3..dc0fa190 100644
--- a/Cunkebao/dist/index.html
+++ b/Cunkebao/dist/index.html
@@ -11,13 +11,13 @@
-
+
-
-
+
+
-
+
diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss
index 6a60b1ef..c973b4bc 100644
--- a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss
+++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss
@@ -1,50 +1,100 @@
-.headerContainer {
- background: #fff;
- border-bottom: 1px solid #f0f0f0;
-}
-
-.searchBar {
- padding: 16px 16px 8px;
+.sidebarMenu {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
background: #fff;
- :global(.ant-input) {
- border-radius: 20px;
- background: #f5f5f5;
- border: none;
+ .headerContainer {
+ padding: 16px;
+ background: #fff;
+ border-bottom: 1px solid #f0f0f0;
- &:focus {
+ .searchBar {
+ margin-bottom: 16px;
+ padding: 0;
background: #fff;
- border: 1px solid #1890ff;
- box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+
+ :global(.ant-input) {
+ border-radius: 20px;
+ background: #f5f5f5;
+ border: none;
+
+ &:focus {
+ background: #fff;
+ border: 1px solid #1890ff;
+ box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+ }
+ }
+ }
+
+ .tabsContainer {
+ display: flex;
+ justify-content: space-around;
+ border-bottom: 1px solid #f0f0f0;
+ padding: 0 0 8px;
+
+ .tabItem {
+ padding: 8px 0;
+ flex: 1;
+ text-align: center;
+ cursor: pointer;
+ color: #999;
+ transition: all 0.3s;
+
+ &:hover {
+ color: #1890ff;
+ }
+
+ &.active {
+ color: #1890ff;
+ border-bottom: 2px solid #1890ff;
+ }
+
+ span {
+ margin-left: 4px;
+ }
+ }
}
}
}
-.tabsContainer {
+// 骨架屏样式
+.skeletonContainer {
+ height: 100%;
+ padding: 16px;
display: flex;
- padding: 0 16px 8px;
- border-bottom: 1px solid #f0f0f0;
+ flex-direction: column;
- .tabItem {
+ .searchBarSkeleton {
+ margin-bottom: 16px;
+ }
+
+ .tabsContainerSkeleton {
display: flex;
- align-items: center;
- padding: 8px 12px;
- cursor: pointer;
- border-radius: 4px;
- transition: all 0.3s;
+ justify-content: space-around;
+ margin-bottom: 16px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #f0f0f0;
+ gap: 8px;
+ }
- &:hover {
- color: #1890ff;
- background-color: rgba(24, 144, 255, 0.1);
- }
+ .contactListSkeleton {
+ flex: 1;
+ overflow-y: auto;
- &.active {
- color: #1890ff;
- background-color: rgba(24, 144, 255, 0.1);
- }
+ .contactItemSkeleton {
+ display: flex;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid #f5f5f5;
- span {
- margin-left: 4px;
+ .contactInfoSkeleton {
+ margin-left: 12px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
}
}
}
@@ -59,3 +109,16 @@
padding: 20px;
text-align: center;
}
+
+.contentContainer {
+ flex: 1;
+ overflow-y: auto;
+}
+
+.footer {
+ padding: 10px;
+ text-align: center;
+ border-top: 1px solid #f0f0f0;
+ background: #fff;
+ display: none; /* 默认隐藏底部,如果需要显示可以移除此行 */
+}
diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx
index 41bd4da7..64723fcc 100644
--- a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx
+++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx
@@ -1,5 +1,5 @@
import React, { useState } from "react";
-import { Input } from "antd";
+import { Input, Skeleton } from "antd";
import {
SearchOutlined,
UserOutlined,
@@ -9,7 +9,6 @@ import {
import { ContractData } from "@/pages/pc/ckbox/data";
import WechatFriendsModule from "./WechatFriendsModule";
import MessageList from "./MessageList/index";
-import LayoutFiexd from "@/components/Layout/LayoutFiexd";
import styles from "./SidebarMenu.module.scss";
import { getChatSessions } from "@/store/module/ckchat";
@@ -53,6 +52,51 @@ const SidebarMenu: React.FC = ({
);
};
+ // 渲染骨架屏
+ const renderSkeleton = () => (
+
+
+
+
+
+
+
+
+
+
+ {Array(8)
+ .fill(null)
+ .map((_, index) => (
+
+ ))}
+
+
+ );
+
// 渲染Header部分,包含搜索框和标签页切换
const renderHeader = () => (
@@ -125,10 +169,15 @@ const SidebarMenu: React.FC
= ({
}
};
+ if (loading) {
+ return renderSkeleton();
+ }
+
return (
-
- {renderContent()}
-
+
+ {renderHeader()}
+
{renderContent()}
+
);
};
diff --git a/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.module.scss b/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.module.scss
new file mode 100644
index 00000000..3c0329a0
--- /dev/null
+++ b/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.module.scss
@@ -0,0 +1,111 @@
+.skeletonLayout {
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+
+ .skeletonHeader {
+ height: 64px;
+ padding: 0 24px;
+ display: flex;
+ align-items: center;
+ background-color: #fff;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .skeletonVerticalSider {
+ background-color: #fff;
+ border-right: 1px solid #f0f0f0;
+
+ .verticalUserList {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px 0;
+
+ .verticalUserItem {
+ margin-bottom: 16px;
+ }
+ }
+ }
+
+ .skeletonSider {
+ background-color: #fff;
+ border-right: 1px solid #f0f0f0;
+ padding: 16px;
+
+ .searchSkeleton {
+ margin-bottom: 16px;
+ }
+
+ .tabsSkeleton {
+ display: flex;
+ justify-content: space-around;
+ margin-bottom: 16px;
+ }
+
+ .contactListSkeleton {
+ .contactItemSkeleton {
+ display: flex;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid #f5f5f5;
+
+ .contactInfoSkeleton {
+ margin-left: 12px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+ }
+ }
+ }
+
+ .skeletonMainContent {
+ background-color: #f5f5f5;
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+
+ .chatHeaderSkeleton {
+ background-color: #fff;
+ padding: 16px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ border-radius: 8px 8px 0 0;
+ }
+
+ .chatContentSkeleton {
+ flex: 1;
+ background-color: #fff;
+ padding: 16px;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+
+ .messageSkeleton {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+
+ &.leftMessage {
+ align-self: flex-start;
+ }
+
+ &.rightMessage {
+ align-self: flex-end;
+ flex-direction: row-reverse;
+ }
+ }
+ }
+
+ .inputAreaSkeleton {
+ background-color: #fff;
+ padding: 16px;
+ border-radius: 0 0 8px 8px;
+ border-top: 1px solid #f0f0f0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx
new file mode 100644
index 00000000..f4ea1f1d
--- /dev/null
+++ b/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx
@@ -0,0 +1,91 @@
+import React from 'react';
+import { Skeleton, Layout } from 'antd';
+import styles from './index.module.scss';
+import pageStyles from '../../index.module.scss';
+
+const { Header, Content, Sider } = Layout;
+
+interface PageSkeletonProps {
+ loading: boolean;
+ children: React.ReactNode;
+}
+
+/**
+ * 页面骨架屏组件
+ * 在数据加载完成前显示骨架屏
+ */
+const PageSkeleton: React.FC = ({ loading, children }) => {
+ if (!loading) return <>{children}>;
+
+ return (
+
+
+
+ {/* 垂直侧边栏骨架 */}
+
+
+ {Array(5)
+ .fill(null)
+ .map((_, index) => (
+
+
+
+ ))}
+
+
+
+ {/* 左侧联系人边栏骨架 */}
+
+
+
+
+
+
+
+
+
+ {Array(8)
+ .fill(null)
+ .map((_, index) => (
+
+ ))}
+
+
+
+ {/* 主内容区骨架 */}
+
+
+
+
+
+
+ {Array(5)
+ .fill(null)
+ .map((_, index) => (
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+};
+
+export default PageSkeleton;
\ No newline at end of file
diff --git a/Cunkebao/src/pages/pc/ckbox/index.tsx b/Cunkebao/src/pages/pc/ckbox/index.tsx
index 1c318615..8b4ba820 100644
--- a/Cunkebao/src/pages/pc/ckbox/index.tsx
+++ b/Cunkebao/src/pages/pc/ckbox/index.tsx
@@ -1,15 +1,17 @@
import React, { useState, useEffect } from "react";
-import { Layout, Button, Space, message, Tooltip, Spin } from "antd";
+import { Layout, Button, Space, message, Tooltip } from "antd";
import { InfoCircleOutlined, MessageOutlined } from "@ant-design/icons";
import dayjs from "dayjs";
import { ContractData } from "./data";
import ChatWindow from "./components/ChatWindow/index";
import SidebarMenu from "./components/SidebarMenu/index";
import VerticalUserList from "./components/VerticalUserList";
+import PageSkeleton from "./components/Skeleton";
import styles from "./index.module.scss";
import { addChatSession } from "@/store/module/ckchat";
const { Header, Content, Sider } = Layout;
import { chatInitAPIdata } from "./main";
+import { KfUserListData } from "@/store/module/ckchat.data";
const CkboxPage: React.FC = () => {
const [messageApi, contextHolder] = message.useMessage();
@@ -25,7 +27,17 @@ const CkboxPage: React.FC = () => {
setLoading(true);
chatInitAPIdata()
.then(response => {
- const { contractList, chatRoomList, kfUserList } = response;
+ const data = response as {
+ contractList: any[];
+ groupList: any[];
+ kfUserList: KfUserListData[];
+ newContractList: { groupName: string; contacts: any[] }[];
+ };
+ const { contractList, groupList, kfUserList, newContractList } = data;
+ response;
+
+ console.log(contractList, groupList, kfUserList, newContractList);
+
//找出已经在聊天的
const isChatList = contractList.filter(
v => (v?.config && v.config?.chat) || false,
@@ -77,66 +89,69 @@ const CkboxPage: React.FC = () => {
};
return (
-
- {contextHolder}
-
-
- {/* 垂直侧边栏 */}
+
+
+ {contextHolder}
+
+
+ {/* 垂直侧边栏 */}
-
-
-
+
+
+
- {/* 左侧联系人边栏 */}
-
-
-
+ {/* 左侧联系人边栏 */}
+
+
+
- {/* 主内容区 */}
-
- {currentChat ? (
-
-
-
-
- }
- onClick={() => setShowProfile(!showProfile)}
- size="small"
- >
- {showProfile ? "隐藏资料" : "显示资料"}
-
-
-
+ {/* 主内容区 */}
+
+ {currentChat ? (
+
+
+
+
+ }
+ onClick={() => setShowProfile(!showProfile)}
+ size="small"
+ >
+ {showProfile ? "隐藏资料" : "显示资料"}
+
+
+
+
+
setShowProfile(!showProfile)}
+ />
- setShowProfile(!showProfile)}
- />
-
- ) : (
-
-
-
-
欢迎使用触客宝
-
选择一个联系人开始聊天
+ ) : (
+
+
+
+
欢迎使用触客宝
+
选择一个联系人开始聊天
+
-
- )}
-
+ )}
+
+
-
+
);
};
diff --git a/Cunkebao/src/store/module/ckchat.ts b/Cunkebao/src/store/module/ckchat.ts
index 13289ac5..22d66f04 100644
--- a/Cunkebao/src/store/module/ckchat.ts
+++ b/Cunkebao/src/store/module/ckchat.ts
@@ -15,6 +15,11 @@ export const useCkChatStore = createPersistStore
(
contractList: [], //联系人列表
chatSessions: [], //聊天会话
kfUserList: [], //客服列表
+ newContractList: [], //联系人分组
+ // 异步设置会话列表
+ asyncNewContractList: data => {
+ set({ newContractList: data });
+ },
// 异步设置会话列表
asyncChatSessions: data => {
set({ chatSessions: data });
@@ -182,3 +187,6 @@ export const asyncContractList = (data: ContractData[]) =>
useCkChatStore.getState().asyncContractList(data);
export const asyncChatSessions = (data: ContractData[]) =>
useCkChatStore.getState().asyncChatSessions(data);
+export const asyncNewContractList = (
+ data: { groupName: string; contacts: any[] }[],
+) => useCkChatStore.getState().asyncNewContractList(data);