优化客户管理模块:更新标签页样式,调整按钮组件,重构联系人展示为表格形式,提升用户体验和代码可读性。

This commit is contained in:
超级老白兔
2025-10-11 15:49:05 +08:00
parent b067134ae7
commit 31c7980a9a
3 changed files with 117 additions and 130 deletions

View File

@@ -114,9 +114,9 @@
// 标签页 // 标签页
.tabs { .tabs {
padding: 20px;
display: flex; display: flex;
gap: 0; gap: 10px;
border-bottom: 1px solid #f0f0f0;
.tab { .tab {
padding: 12px 24px; padding: 12px 24px;
@@ -127,15 +127,6 @@
color: #8c8c8c; color: #8c8c8c;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
&:hover {
color: #1890ff;
}
&.activeTab {
color: #1890ff;
border-bottom-color: #1890ff;
}
} }
} }

View File

@@ -1,13 +1,8 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import PowerNavigation from "@/components/PowerNavtion"; import PowerNavigation from "@/components/PowerNavtion";
import { import { SearchOutlined, FilterOutlined } from "@ant-design/icons";
SearchOutlined,
FilterOutlined,
MessageOutlined,
PhoneOutlined,
} from "@ant-design/icons";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { Button, Input, Row, Col, Pagination, Spin, message } from "antd"; import { Button, Input, Table, message } from "antd";
import { getContactList } from "@/pages/pc/ckbox/weChat/api"; import { getContactList } from "@/pages/pc/ckbox/weChat/api";
import { ContractData } from "@/pages/pc/ckbox/data"; import { ContractData } from "@/pages/pc/ckbox/data";
import Layout from "@/components/Layout/LayoutFiexd"; import Layout from "@/components/Layout/LayoutFiexd";
@@ -196,132 +191,129 @@ const CustomerManagement: React.FC = () => {
</Button> </Button>
</div> </div>
{/* 标签 */} {/* 标签按钮组 */}
<div className={styles.tabs}> <div className={styles.tabs}>
{tabs.map(tab => ( {tabs.map(tab => (
<button <Button
key={tab.key} key={tab.key}
className={`${styles.tab} ${activeTab === tab.key ? styles.activeTab : ""}`} type={activeTab === tab.key ? "primary" : "default"}
onClick={() => setActiveTab(tab.key)} onClick={() => setActiveTab(tab.key)}
> >
{tab.label} ({tab.count}) {tab.label}
</button> <span style={{ marginLeft: 6, opacity: 0.85 }}>
{tab.count}
</span>
</Button>
))} ))}
</div> </div>
</div> </div>
</> </>
} }
footer={ footer={null}
<div className="pagination-wrapper">
<Pagination
current={pagination.current}
pageSize={pagination.pageSize}
total={pagination.total}
showSizeChanger
showQuickJumper
showTotal={(total, range) =>
`${range[0]}-${range[1]} 条,共 ${total}`
}
onChange={(page, pageSize) => {
loadContacts(page, pageSize || pagination.pageSize);
}}
onShowSizeChange={(current, size) => {
loadContacts(1, size);
}}
pageSizeOptions={["6", "12", "24", "48"]}
/>
</div>
}
> >
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> <div className={styles.content}>
{/* 联系人卡片列表 */} {/* 联系人表 */}
<div className={styles.contactsList}> <Table
{loading ? ( rowKey={(record: any) => record.id || record.serverId}
<div style={{ textAlign: "center", padding: "50px" }}> loading={loading}
<Spin size="large" /> dataSource={filteredContacts as any}
<p style={{ marginTop: "16px", color: "#666" }}> columns={[
... {
</p> title: "客户姓名",
</div> key: "name",
) : filteredContacts.length === 0 ? ( render: (_: any, record: any) => {
<div style={{ textAlign: "center", padding: "50px" }}> const displayName =
<p style={{ color: "#999" }}></p> record.conRemark ||
</div> record.nickname ||
) : ( record.alias ||
<> "未知用户";
<Row gutter={[16, 16]}> return (
{filteredContacts.map(contact => ( <div className={styles.contactInfo}>
<Col span={8} key={contact.id || contact.serverId}> <Avatar
<div className={styles.contactCard}> name={displayName}
<div className={styles.cardHeader}> avatar={record.avatar}
<div className={styles.contactInfo}> size={40}
<Avatar />
name={ <div className={styles.nameSection}>
contact.conRemark || <h3 className={styles.contactName}>{displayName}</h3>
contact.nickname || <p className={styles.roleCompany}>
contact.alias || · {record.desc || "未设置公司"}
"未知用户" </p>
}
avatar={contact.avatar}
size={48}
/>
<div className={styles.nameSection}>
<h3 className={styles.contactName}>
{contact.conRemark ||
contact.nickname ||
contact.alias ||
"未知用户"}
</h3>
<p className={styles.roleCompany}>
{"·"} {contact.desc || "未设置公司"}
</p>
</div>
</div>
</div>
<div className={styles.contactDetails}>
<div className={styles.contactInfo}>
<p className={styles.contactItem}>
<span className={styles.label}>:</span>{" "}
{contact.phone || "未设置电话"}
</p>
<p className={styles.contactItem}>
<span className={styles.label}>:</span>{" "}
{contact.region || contact.city || "未设置地区"}
</p>
<p className={styles.contactItem}>
<span className={styles.label}>ID:</span>{" "}
{contact.wechatId}
</p>
</div>
</div>
<div className={styles.tagsSection}>
<div className={styles.tags}>
{contact?.labels?.map(
(tag: string, index: number) => (
<span key={index} className={styles.tag}>
{tag}
</span>
),
)}
</div>
</div>
<div className={styles.actions}>
<Button type="primary" block>
<MessageOutlined />
</Button>
</div>
</div> </div>
</Col> </div>
))} );
</Row> },
</> },
)} {
</div> title: "RFM评分",
dataIndex: "rfmScore",
key: "rfmScore",
width: 100,
render: (val: any) => val ?? "-",
},
{
title: "电话",
dataIndex: "phone",
key: "phone",
width: 180,
render: (val: string) => val || "未设置电话",
},
{
title: "微信号",
dataIndex: "wechatId",
key: "wechatId",
width: 200,
},
{
title: "地址",
key: "address",
ellipsis: true,
render: (_: any, record: any) =>
record.region || record.city || "未设置地区",
},
{
title: "标签",
key: "labels",
render: (_: any, record: any) => (
<div className={styles.tags}>
{(record?.labels || []).map(
(tag: string, index: number) => (
<span key={index} className={styles.tag}>
{tag}
</span>
),
)}
</div>
),
},
{
title: "操作",
key: "action",
width: 120,
render: () => (
<Button type="primary" size="small">
</Button>
),
},
]}
pagination={{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number, range: [number, number]) =>
`${range[0]}-${range[1]} 条,共 ${total}`,
pageSizeOptions: ["6", "12", "24", "48"],
}}
onChange={(pager: any) => {
const nextCurrent = pager.current || 1;
const nextSize = pager.pageSize || pagination.pageSize;
loadContacts(nextCurrent, nextSize);
}}
/>
</div> </div>
</div> </div>
</Layout> </Layout>

View File

@@ -5,6 +5,7 @@ import AudioMessage from "./components/AudioMessage/AudioMessage";
import SmallProgramMessage from "./components/SmallProgramMessage"; import SmallProgramMessage from "./components/SmallProgramMessage";
import VideoMessage from "./components/VideoMessage"; import VideoMessage from "./components/VideoMessage";
import ClickMenu from "./components/ClickMeau"; import ClickMenu from "./components/ClickMeau";
import LocationMessage from "./components/LocationMessage";
import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
import { formatWechatTime } from "@/utils/common"; import { formatWechatTime } from "@/utils/common";
import { getEmojiPath } from "@/components/EmojiSeclection/wechatEmoji"; import { getEmojiPath } from "@/components/EmojiSeclection/wechatEmoji";
@@ -270,6 +271,9 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
} }
return renderErrorMessage("[表情包]"); return renderErrorMessage("[表情包]");
case 48: // 定位消息
return <LocationMessage content={content || ""} />;
case 49: // 小程序/文章/其他:图文、文件 case 49: // 小程序/文章/其他:图文、文件
return <SmallProgramMessage content={content || ""} />; return <SmallProgramMessage content={content || ""} />;