diff --git a/Touchkebao/src/api/module/wechat.ts b/Touchkebao/src/api/module/wechat.ts new file mode 100644 index 00000000..5e10f84c --- /dev/null +++ b/Touchkebao/src/api/module/wechat.ts @@ -0,0 +1,5 @@ +import request from "@/api/request"; +//获取客服列表 +export function getCustomerList() { + return request("/v1/kefu/customerService/list", {}, "GET"); +} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts index 7b153312..8ce1bcac 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts @@ -63,7 +63,7 @@ export function getMessageList(params: { page: number; limit: number }) { } //获取客服列表 -export function getAgentList() { +export function getCustomerList() { return request("/v1/kefu/customerService/list", {}, "GET"); } diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/VerticalUserList.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss similarity index 61% rename from Touchkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/VerticalUserList.module.scss rename to Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss index cf6db02e..9cc2941e 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/VerticalUserList.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss @@ -1,4 +1,4 @@ -.verticalUserList { +.customerList { display: flex; flex-direction: column; height: 100%; @@ -91,4 +91,52 @@ background-color: #8c8c8c; // 灰色表示离线 } } + + // 骨架屏样式 + .skeletonContainer { + display: flex; + flex-direction: column; + align-items: center; + padding: 10px 0; + } + + .skeletonItem { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 10px 0; + position: relative; + margin-bottom: 10px; + } + + .skeletonAvatar { + width: 50px; + height: 50px; + border-radius: 50%; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: skeleton-loading 1.5s infinite; + } + + .skeletonIndicator { + position: absolute; + bottom: 10px; + right: 10px; + width: 8px; + height: 8px; + border-radius: 50%; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: skeleton-loading 1.5s infinite; + } + + @keyframes skeleton-loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } + } } diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx new file mode 100644 index 00000000..37c38d6e --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx @@ -0,0 +1,122 @@ +import React, { useEffect, useState } from "react"; +import { Avatar, Badge } from "antd"; +import styles from "./com.module.scss"; +import { + useCustomerStore, + updateCurrentCustomer, + updateCustomerList, +} from "@weChatStore/customer"; +import { getChatSessions } from "@storeModule/ckchat/ckchat"; +import { getCustomerList } from "@apiModule/wechat"; +const CustomerList: React.FC = () => { + const [loading, setLoading] = useState(true); + + //初始化获取客服列表 + useEffect(() => { + setLoading(true); + getCustomerList() + .then(res => { + updateCustomerList(res); + setLoading(false); + }) + .catch(() => { + setLoading(false); + }); + }, []); + const handleUserSelect = (userId: number) => { + updateCurrentCustomer( + customerList.find(customer => customer.id === userId) || null, + ); + }; + const customerList = useCustomerStore(state => state.customerList); + const currentCustomer = useCustomerStore(state => state.currentCustomer); + + const chatSessions = getChatSessions(); + const getUnreadCount = (customerId: number) => { + if (currentCustomer?.id != 0) { + const session = chatSessions.filter( + v => v.wechatAccountId === customerId, + ); + return session.reduce((pre, cur) => pre + cur.config.unreadCount, 0); + } else { + return chatSessions.reduce((pre, cur) => pre + cur.config.unreadCount, 0); + } + }; + + // 骨架屏组件 + const SkeletonItem = () => ( +
+
+
+
+ ); + + // 骨架屏列表 + const SkeletonList = () => ( +
+ + + + + +
+ ); + + return ( +
+
+
微信号
+
+
+ {loading ? ( + + ) : ( + <> +
handleUserSelect(0)} + > + +
全部
+
+
+
+ {customerList.map(customer => ( +
handleUserSelect(customer.id)} + > + + + {!customer.avatar && customer.name.charAt(0)} + + +
+
+ ))} + + )} +
+
+ ); +}; + +export default CustomerList; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/index.tsx deleted file mode 100644 index e75e7d08..00000000 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/index.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from "react"; -import { Avatar, Badge } from "antd"; -import styles from "./VerticalUserList.module.scss"; -import { useCkChatStore, asyncKfSelected } from "@/store/module/ckchat/ckchat"; - -const VerticalUserList: React.FC = () => { - const handleUserSelect = (userId: number) => { - asyncKfSelected(userId); - }; - const kfUserList = useCkChatStore(state => state.kfUserList); - const kfSelected = useCkChatStore(state => state.kfSelected); - const chatSessions = useCkChatStore(state => state.chatSessions); - const getUnreadCount = (wechatAccountId: number) => { - if (wechatAccountId != 0) { - const session = chatSessions.filter( - v => v.wechatAccountId === wechatAccountId, - ); - return session.reduce((pre, cur) => pre + cur.config.unreadCount, 0); - } else { - return chatSessions.reduce((pre, cur) => pre + cur.config.unreadCount, 0); - } - }; - - return ( -
-
-
微信号
-
-
-
handleUserSelect(0)}> - -
全部
-
-
-
- {kfUserList.map(user => ( -
handleUserSelect(user.id)} - > - - - {!user.avatar && user.name.charAt(0)} - - -
-
- ))} -
-
- ); -}; - -export default VerticalUserList; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx index 25fc7774..c28d1a5d 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx @@ -3,7 +3,7 @@ import { Layout } from "antd"; import { MessageOutlined } from "@ant-design/icons"; import ChatWindow from "./components/ChatWindow/index"; import SidebarMenu from "./components/SidebarMenu/index"; -import VerticalUserList from "./components/VerticalUserList"; +import CustomerList from "./components/CustomerList"; import PageSkeleton from "./components/Skeleton"; import styles from "./index.module.scss"; const { Content, Sider } = Layout; @@ -16,9 +16,9 @@ const CkboxPage: React.FC = () => { const currentContract = useWeChatStore(state => state.currentContract); useEffect(() => { // 方法一:使用 Promise 链式调用处理异步函数 - if (!getIsLoadWeChat()) { - setLoading(true); - } + // if (!getIsLoadWeChat()) { + // setLoading(true); + // } chatInitAPIdata() .then(() => { // 数据加载完成后初始化WebSocket连接 @@ -39,7 +39,7 @@ const CkboxPage: React.FC = () => { {/* 垂直侧边栏 */} - + {/* 左侧联系人边栏 */} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts index 11c4082b..de25c845 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts @@ -1,5 +1,4 @@ import { - asyncKfUserList, asyncContractList, asyncChatSessions, asyncWeChatGroup, @@ -16,7 +15,7 @@ import { getControlTerminalList, getContactList, getGroupList, - getAgentList, + getCustomerList, getLabelsListByGroup, getMessageList, } from "./api"; @@ -27,6 +26,7 @@ import { weChatGroup, } from "@/pages/pc/ckbox/data"; +import { updateCustomerList } from "@weChatStore/customer"; //获取触客宝基础信息 export const chatInitAPIdata = async () => { try { @@ -52,12 +52,11 @@ export const chatInitAPIdata = async () => { await asyncWeChatGroup(groupList); - //获取控制终端列表 - const kfUserList: KfUserListData[] = await getAgentList(); - - //获取用户列表 - await asyncKfUserList(kfUserList); + // //获取控制终端列表 + // const kfUserList: KfUserListData[] = await getCustomerList(); + // //获取用户列表 + // updateCustomerList(kfUserList); //获取标签列表 const countLables = await getCountLables(); await asyncCountLables(countLables); diff --git a/Touchkebao/src/store/module/weChat/chatRecord.ts b/Touchkebao/src/store/module/weChat/chatRecord.ts new file mode 100644 index 00000000..e69de29b diff --git a/Touchkebao/src/store/module/weChat/contacts.ts b/Touchkebao/src/store/module/weChat/contacts.ts new file mode 100644 index 00000000..e69de29b diff --git a/Touchkebao/src/store/module/weChat/currentChat.ts b/Touchkebao/src/store/module/weChat/currentChat.ts new file mode 100644 index 00000000..e69de29b diff --git a/Touchkebao/src/store/module/weChat/customer.data.ts b/Touchkebao/src/store/module/weChat/customer.data.ts new file mode 100644 index 00000000..50983be6 --- /dev/null +++ b/Touchkebao/src/store/module/weChat/customer.data.ts @@ -0,0 +1,41 @@ +export interface Customer { + id: number; + tenantId: number; + wechatId: string; + nickname: string; + alias: string; + avatar: string; + gender: number; + region: string; + signature: string; + bindQQ: string; + bindEmail: string; + bindMobile: string; + createTime: string; + currentDeviceId: number; + isDeleted: boolean; + deleteTime: string; + groupId: number; + memo: string; + wechatVersion: string; + labels: string[]; + lastUpdateTime: string; + isOnline?: boolean; + momentsMax: number; + momentsNum: number; + [key: string]: any; +} + +//Store State +export interface CustomerState { + //客服列表 + customerList: Customer[]; + //当前选中的客服 + currentCustomer: Customer | null; + //更新客服列表 + updateCustomerList: (customerList: Customer[]) => void; + //更新客服状态 + updateCustomerStatus: (customerId: number, status: string) => void; + //更新当前选中的客服 + updateCurrentCustomer: (customer: Customer) => void; +} diff --git a/Touchkebao/src/store/module/weChat/customer.ts b/Touchkebao/src/store/module/weChat/customer.ts new file mode 100644 index 00000000..9b85daf7 --- /dev/null +++ b/Touchkebao/src/store/module/weChat/customer.ts @@ -0,0 +1,56 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { Customer, CustomerState } from "./customer.data"; +export const useCustomerStore = create()( + persist( + (set, get) => ({ + customerList: [], //客服列表 + currentCustomer: null, //当前选中的客服 + updateCustomerList: (customerList: Customer[]) => set({ customerList }), //更新客服列表 + updateCurrentCustomer: (customer: Customer) => + set({ currentCustomer: customer }), //更新当前选中的客服 + updateCustomerStatus: (customerId: number, status: string) => + set({ + customerList: get().customerList.map(customer => + customer.id === customerId ? { ...customer, status } : customer, + ), + }), //更新客服状态 + }), + { + name: "customer-storage", + partialize: state => ({ + customerList: [], + currentCustomer: null, + }), + }, + ), +); +/** + * 更新当前选中的客服 + * @param customer 客服 + * @returns void + */ +export const updateCurrentCustomer = (customer: Customer) => + useCustomerStore.getState().updateCurrentCustomer(customer); +/** + * 更新客服列表 + * @param customerList 客服列表 + * @returns void + */ +export const updateCustomerList = (customerList: Customer[]) => + useCustomerStore.getState().updateCustomerList(customerList); +/** + * 获取当前选中的客服 + * @returns Customer | null + */ +export const getCurrentCustomer = () => + useCustomerStore.getState().currentCustomer; + +/** + * 更新客服状态 + * @param customerId 客服ID + * @param status 状态 + * @returns void + */ +export const updateCustomerStatus = (customerId: number, status: string) => + useCustomerStore.getState().updateCustomerStatus(customerId, status); diff --git a/Touchkebao/src/store/module/weChat/message.ts b/Touchkebao/src/store/module/weChat/message.ts new file mode 100644 index 00000000..e69de29b diff --git a/Touchkebao/tsconfig.json b/Touchkebao/tsconfig.json index 9d100420..f6e4f581 100644 --- a/Touchkebao/tsconfig.json +++ b/Touchkebao/tsconfig.json @@ -17,7 +17,10 @@ "jsx": "react-jsx", "baseUrl": "./", "paths": { - "@/*": ["src/*"] + "@/*": ["src/*"], + "@weChatStore/*": ["src/store/module/weChat/*"], + "@storeModule/*": ["src/store/module/*"], + "@apiModule/*": ["src/api/module/*"] } }, "include": ["src"] diff --git a/Touchkebao/vite.config.ts b/Touchkebao/vite.config.ts index 7ab397b4..1730de58 100644 --- a/Touchkebao/vite.config.ts +++ b/Touchkebao/vite.config.ts @@ -7,6 +7,9 @@ export default defineConfig({ resolve: { alias: { "@": path.resolve("src"), + "@storeModule": path.resolve("src/store/module/"), + "@weChatStore": path.resolve("src/store/module/weChat"), + "@apiModule": path.resolve("src/api/module/"), }, }, server: {