FEAT => 本次更新项目为:
This commit is contained in:
48
Cunkebao/src/components/Layout/LayoutFiexd.tsx
Normal file
48
Cunkebao/src/components/Layout/LayoutFiexd.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from "react";
|
||||
import { SpinLoading } from "antd-mobile";
|
||||
import styles from "./layout.module.scss";
|
||||
|
||||
interface LayoutProps {
|
||||
loading?: boolean;
|
||||
children?: React.ReactNode;
|
||||
header?: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
}
|
||||
|
||||
const LayoutFiexd: React.FC<LayoutProps> = ({
|
||||
header,
|
||||
children,
|
||||
footer,
|
||||
loading = false,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div className="header">{header}</div>
|
||||
<div
|
||||
className="content"
|
||||
style={{
|
||||
flex: 1,
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<div className={styles.loadingContainer}>
|
||||
<SpinLoading color="primary" style={{ fontSize: 32 }} />
|
||||
<div className={styles.loadingText}>加载中...</div>
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
<div className="footer">{footer}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutFiexd;
|
||||
@@ -1,63 +1,61 @@
|
||||
.sidebar {
|
||||
.headerContainer {
|
||||
background: #fff;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.searchBar {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
.searchBar {
|
||||
padding: 16px 16px 8px;
|
||||
background: #fff;
|
||||
|
||||
:global(.ant-input) {
|
||||
border-radius: 20px;
|
||||
background: #f5f5f5;
|
||||
border: none;
|
||||
: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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:global(.ant-tabs-content) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-tabs-tabpane) {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-tabs-nav) {
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
&:focus {
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
:global(.ant-tabs-tab) {
|
||||
padding: 12px 0;
|
||||
margin: 0 16px 0 0;
|
||||
}
|
||||
border: 1px solid #1890ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tabsContainer {
|
||||
display: flex;
|
||||
padding: 0 16px 8px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.tabItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #1890ff;
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { Layout, Input, Tabs } from "antd";
|
||||
import { Layout as AntLayout, Input, Tabs } from "antd";
|
||||
import {
|
||||
SearchOutlined,
|
||||
UserOutlined,
|
||||
@@ -9,9 +9,10 @@ import {
|
||||
import { ContactData } from "./data";
|
||||
import WechatFriendsModule from "./WechatFriendsModule";
|
||||
import MessageList from "../MessageList/index";
|
||||
import LayoutFiexd from "@/components/Layout/LayoutFiexd";
|
||||
import styles from "./SidebarMenu.module.scss";
|
||||
|
||||
const { Sider } = Layout;
|
||||
const { Sider } = AntLayout;
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
interface SidebarMenuProps {
|
||||
@@ -54,8 +55,9 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider width={300} className={styles.sidebar}>
|
||||
// 渲染Header部分,包含搜索框和标签页切换
|
||||
const renderHeader = () => (
|
||||
<div className={styles.headerContainer}>
|
||||
{/* 搜索栏 */}
|
||||
<div className={styles.searchBar}>
|
||||
<Input
|
||||
@@ -67,58 +69,68 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 标签页 */}
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
className={styles.tabs}
|
||||
>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<MessageOutlined />
|
||||
聊天
|
||||
</span>
|
||||
}
|
||||
key="chats"
|
||||
{/* 标签页切换 */}
|
||||
<div className={styles.tabsContainer}>
|
||||
<div
|
||||
className={`${styles.tabItem} ${activeTab === "chats" ? styles.active : ""}`}
|
||||
onClick={() => setActiveTab("chats")}
|
||||
>
|
||||
<MessageOutlined />
|
||||
<span>聊天</span>
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${activeTab === "contacts" ? styles.active : ""}`}
|
||||
onClick={() => setActiveTab("contacts")}
|
||||
>
|
||||
<UserOutlined />
|
||||
<span>联系人</span>
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${activeTab === "groups" ? styles.active : ""}`}
|
||||
onClick={() => setActiveTab("groups")}
|
||||
>
|
||||
<TeamOutlined />
|
||||
<span>群组</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// 渲染内容部分
|
||||
const renderContent = () => {
|
||||
switch (activeTab) {
|
||||
case "chats":
|
||||
return (
|
||||
<MessageList
|
||||
chatSessions={getFilteredSessions()}
|
||||
onChatSelect={onChatSelect}
|
||||
currentChat={currentChat}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<UserOutlined />
|
||||
联系人
|
||||
</span>
|
||||
}
|
||||
key="contacts"
|
||||
>
|
||||
);
|
||||
case "contacts":
|
||||
return (
|
||||
<WechatFriendsModule
|
||||
contacts={getFilteredContacts()}
|
||||
onContactClick={onContactClick}
|
||||
selectedContactId={currentChat}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<TeamOutlined />
|
||||
群组
|
||||
</span>
|
||||
}
|
||||
key="groups"
|
||||
>
|
||||
);
|
||||
case "groups":
|
||||
return (
|
||||
<div className={styles.emptyState}>
|
||||
<TeamOutlined style={{ fontSize: 48, color: "#ccc" }} />
|
||||
<p>暂无群组</p>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Sider>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<LayoutFiexd header={renderHeader()} footer="底部">
|
||||
{renderContent()}
|
||||
</LayoutFiexd>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
.ckboxLayout {
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.sidebar {
|
||||
.header {
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sider {
|
||||
background: #fff;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -87,6 +104,8 @@
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
|
||||
.chatContainer {
|
||||
height: 100%;
|
||||
|
||||
@@ -7,7 +7,7 @@ import ChatWindow from "./components/ChatWindow/index";
|
||||
import SidebarMenu from "./components/SidebarMenu/index";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
const { Content } = Layout;
|
||||
const { Header, Content, Sider } = Layout;
|
||||
import { loginWithToken, clearUnreadCount, getMessages } from "./api";
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
import { chatInitAPIdata } from "./main";
|
||||
@@ -85,53 +85,57 @@ const CkboxPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<Layout className={styles.ckboxLayout}>
|
||||
{/* <Button onClick={getChatInfo}>点击</Button> */}
|
||||
{contextHolder}
|
||||
{/* 左侧边栏 */}
|
||||
<SidebarMenu
|
||||
contacts={contacts}
|
||||
chatSessions={chatSessions}
|
||||
currentChat={currentChat}
|
||||
onContactClick={handleContactClick}
|
||||
onChatSelect={setCurrentChat}
|
||||
loading={loading}
|
||||
/>
|
||||
<Header className={styles.header}>触客宝</Header>
|
||||
<Layout>
|
||||
{/* 左侧边栏 */}
|
||||
<Sider width={280} className={styles.sider}>
|
||||
<SidebarMenu
|
||||
contacts={contacts}
|
||||
chatSessions={chatSessions}
|
||||
currentChat={currentChat}
|
||||
onContactClick={handleContactClick}
|
||||
onChatSelect={setCurrentChat}
|
||||
loading={loading}
|
||||
/>
|
||||
</Sider>
|
||||
|
||||
{/* 主内容区 */}
|
||||
<Content className={styles.mainContent}>
|
||||
{currentChat ? (
|
||||
<div className={styles.chatContainer}>
|
||||
<div className={styles.chatToolbar}>
|
||||
<Space>
|
||||
<Tooltip title={showProfile ? "隐藏资料" : "显示资料"}>
|
||||
<Button
|
||||
type={showProfile ? "primary" : "default"}
|
||||
icon={<InfoCircleOutlined />}
|
||||
onClick={() => setShowProfile(!showProfile)}
|
||||
size="small"
|
||||
>
|
||||
{showProfile ? "隐藏资料" : "显示资料"}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
{/* 主内容区 */}
|
||||
<Content className={styles.mainContent}>
|
||||
{currentChat ? (
|
||||
<div className={styles.chatContainer}>
|
||||
<div className={styles.chatToolbar}>
|
||||
<Space>
|
||||
<Tooltip title={showProfile ? "隐藏资料" : "显示资料"}>
|
||||
<Button
|
||||
type={showProfile ? "primary" : "default"}
|
||||
icon={<InfoCircleOutlined />}
|
||||
onClick={() => setShowProfile(!showProfile)}
|
||||
size="small"
|
||||
>
|
||||
{showProfile ? "隐藏资料" : "显示资料"}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</div>
|
||||
<ChatWindow
|
||||
chat={currentChat}
|
||||
onSendMessage={handleSendMessage}
|
||||
showProfile={showProfile}
|
||||
onToggleProfile={() => setShowProfile(!showProfile)}
|
||||
/>
|
||||
</div>
|
||||
<ChatWindow
|
||||
chat={currentChat}
|
||||
onSendMessage={handleSendMessage}
|
||||
showProfile={showProfile}
|
||||
onToggleProfile={() => setShowProfile(!showProfile)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.welcomeScreen}>
|
||||
<div className={styles.welcomeContent}>
|
||||
<MessageOutlined style={{ fontSize: 64, color: "#1890ff" }} />
|
||||
<h2>欢迎使用触客宝</h2>
|
||||
<p>选择一个联系人开始聊天</p>
|
||||
) : (
|
||||
<div className={styles.welcomeScreen}>
|
||||
<div className={styles.welcomeContent}>
|
||||
<MessageOutlined style={{ fontSize: 64, color: "#1890ff" }} />
|
||||
<h2>欢迎使用触客宝</h2>
|
||||
<p>选择一个联系人开始聊天</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Content>
|
||||
)}
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user