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