You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

558 lines
22 KiB
JavaScript

3 weeks ago
import React, { useState, useEffect, useRef } from 'react';
import * as echarts from 'echarts';
1 month ago
import styles from './CustomerInfoManagement.less';
const CustomerInfoManagement = () => {
3 weeks ago
const [searchKeyword, setSearchKeyword] = useState('');
const [customerType, setCustomerType] = useState('全部');
const [customerLevel, setCustomerLevel] = useState('全部');
const [cooperationStatus, setCooperationStatus] = useState('全部');
const [selectedRows, setSelectedRows] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
// 图表引用
const customerTypeChartRef = useRef(null);
const customerValueChartRef = useRef(null);
const customerTypeChartInstance = useRef(null);
const customerValueChartInstance = useRef(null);
// KPI数据
const kpiData = {
totalCustomers: 389,
highValueCustomers: 180,
inCooperation: 360,
newThisMonth: 8,
1 month ago
};
3 weeks ago
// 客户类型分布图表配置
const customerTypeChartOption = {
title: {
text: '客户类型分布',
left: 'left',
textStyle: {
fontSize: 16,
fontWeight: 600,
},
1 month ago
},
3 weeks ago
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
1 month ago
},
3 weeks ago
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
1 month ago
},
3 weeks ago
xAxis: {
type: 'category',
data: ['客户', '供应商', '第3周', '第4周'],
axisLabel: {
rotate: 0,
},
},
yAxis: {
type: 'value',
max: 100,
1 month ago
},
3 weeks ago
series: [
{
name: '数量',
type: 'bar',
data: [56, 32, 85, 15],
itemStyle: {
color: '#1890ff',
},
label: {
show: true,
position: 'top',
},
},
],
};
// 客户价值分布环形图配置
const customerValueChartOption = {
title: {
text: '客户价值分布',
left: 'left',
textStyle: {
fontSize: 16,
fontWeight: 600,
},
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)',
},
legend: {
orient: 'vertical',
left: 'right',
top: 'middle',
},
series: [
{
name: '客户价值',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2,
},
label: {
show: false,
},
emphasis: {
label: {
show: true,
fontSize: 16,
fontWeight: 'bold',
},
},
data: [
{ value: 180, name: '高价值客户', itemStyle: { color: '#5B9BD5' } },
{ value: 120, name: '中等客户', itemStyle: { color: '#FFC000' } },
{ value: 89, name: '小客户', itemStyle: { color: '#9E7CC1' } },
],
},
],
};
// 初始化图表
useEffect(() => {
// 初始化客户类型分布图表
if (customerTypeChartRef.current) {
customerTypeChartInstance.current = echarts.init(customerTypeChartRef.current);
customerTypeChartInstance.current.setOption(customerTypeChartOption);
}
// 初始化客户价值分布图表
if (customerValueChartRef.current) {
customerValueChartInstance.current = echarts.init(customerValueChartRef.current);
customerValueChartInstance.current.setOption(customerValueChartOption);
}
// 响应式调整
const handleResize = () => {
customerTypeChartInstance.current?.resize();
customerValueChartInstance.current?.resize();
};
window.addEventListener('resize', handleResize);
// 清理函数
return () => {
window.removeEventListener('resize', handleResize);
customerTypeChartInstance.current?.dispose();
customerValueChartInstance.current?.dispose();
};
}, []);
// 最近活动数据
const recentActivities = [
1 month ago
{
3 weeks ago
id: 1,
title: '新合同签署',
description: '与中石化华东分公司签署年度供应合同',
time: '2小时前',
1 month ago
},
{
3 weeks ago
id: 2,
title: '订单完成',
description: '完成海南石油贸易有限公司的油品配送',
time: '昨天',
1 month ago
},
{
3 weeks ago
id: 3,
title: '客户反馈',
description: '收到华南能源集团的满意度调查回复',
time: '昨天',
1 month ago
},
{
3 weeks ago
id: 4,
title: '新客户注册',
description: '新客户"浙江能源有限公司"完成注册',
time: '3天前',
1 month ago
},
];
3 weeks ago
// 表格数据
1 month ago
const tableData = [
{
3 weeks ago
id: '1',
customerName: '中国石化销售股份有限公司',
contact: '钱亚男',
phone: '18901563341',
classification: '高价值',
monthlyAmount: 1250000,
cooperationStatus: '合作中',
satisfaction: 4.5,
},
{
id: '2',
customerName: '中石化华东分公司',
contact: '郑宇雅',
phone: '15341731282',
classification: '常规客户',
monthlyAmount: 1250000,
cooperationStatus: '合作中',
satisfaction: 4.0,
},
{
id: '3',
customerName: '海南石油贸易有限公司',
contact: '孙向明',
phone: '13252257033',
classification: '高价值',
monthlyAmount: 850000,
cooperationStatus: '合作中',
satisfaction: 4.5,
1 month ago
},
{
3 weeks ago
id: '4',
customerName: '东莞石化有限公司',
contact: '何思颖',
phone: '18931788771',
classification: '高价值',
monthlyAmount: 980000,
cooperationStatus: '合作中',
satisfaction: 4.5,
},
{
id: '5',
customerName: '中国石油化工集团有限公司',
contact: '钱佳仪',
phone: '13743378254',
classification: '常规客户',
monthlyAmount: 980000,
cooperationStatus: '暂停合作',
satisfaction: 4.0,
1 month ago
},
];
3 weeks ago
// 处理选择
const handleSelectRow = (id) => {
if (selectedRows.includes(id)) {
setSelectedRows(selectedRows.filter(rowId => rowId !== id));
} else {
setSelectedRows([...selectedRows, id]);
}
};
// 处理全选
const handleSelectAll = () => {
if (selectedRows.length === tableData.length) {
setSelectedRows([]);
} else {
setSelectedRows(tableData.map(item => item.id));
}
};
// 渲染星级评分
const renderStars = (rating) => {
const stars = [];
const fullStars = Math.floor(rating);
const hasHalfStar = rating % 1 !== 0;
for (let i = 0; i < fullStars; i++) {
stars.push(<span key={i} className={styles.starFull}></span>);
}
if (hasHalfStar) {
stars.push(<span key="half" className={styles.starHalf}></span>);
}
const emptyStars = 5 - Math.ceil(rating);
for (let i = 0; i < emptyStars; i++) {
stars.push(<span key={`empty-${i}`} className={styles.starEmpty}></span>);
}
return stars;
};
// 计算总页数
const totalPages = Math.ceil(85 / pageSize);
1 month ago
return (
<div className={styles.container}>
3 weeks ago
{/* KPI卡片区域 */}
<div className={styles.kpiRow}>
<div className={styles.kpiCard}>
<div className={styles.kpiIcon}>👥</div>
<div className={styles.kpiValue}>{kpiData.totalCustomers}</div>
<div className={styles.kpiTitle}>总客户数</div>
</div>
<div className={styles.kpiCard}>
<div className={styles.kpiIcon}>👑</div>
<div className={styles.kpiValue}>{kpiData.highValueCustomers}</div>
<div className={styles.kpiTitle}>高价值客户</div>
</div>
<div className={styles.kpiCard}>
<div className={styles.kpiIcon}>🤝</div>
<div className={styles.kpiValue}>{kpiData.inCooperation}</div>
<div className={styles.kpiTitle}>合作中</div>
</div>
<div className={styles.kpiCard}>
<div className={styles.kpiIcon}></div>
<div className={styles.kpiValue}>{kpiData.newThisMonth}</div>
<div className={styles.kpiTitle}>本月新增</div>
</div>
</div>
{/* 图表和活动区域 */}
<div className={styles.chartRow}>
{/* 客户类型分布图表 */}
<div className={styles.chartCard}>
<div className={styles.chartHeader}>
<div className={styles.chartTitle}>客户类型分布</div>
<select className={styles.timeSelect} defaultValue="全部时间">
<option value="全部时间">全部时间</option>
<option value="本月">本月</option>
<option value="本季度">本季度</option>
<option value="本年">本年</option>
</select>
</div>
<div
ref={customerTypeChartRef}
className={styles.chartContainer}
/>
</div>
{/* 客户价值分布图表 */}
<div className={styles.chartCard}>
<div className={styles.chartHeader}>
<div className={styles.chartTitle}>客户价值分布</div>
</div>
<div
ref={customerValueChartRef}
className={styles.chartContainer}
/>
</div>
{/* 最近活动列表 */}
<div className={styles.activityCard}>
<div className={styles.chartHeader}>
<div className={styles.chartTitle}>最近活动</div>
</div>
<div className={styles.activityList}>
{recentActivities.map((activity) => (
<div key={activity.id} className={styles.activityItem}>
<div className={styles.activityIcon}>
{activity.id === 1 && '📄'}
{activity.id === 2 && '✅'}
{activity.id === 3 && '💬'}
{activity.id === 4 && '👤'}
</div>
<div className={styles.activityContent}>
<div className={styles.activityTitle}>{activity.title}</div>
<div className={styles.activityDesc}>{activity.description}</div>
<div className={styles.activityTime}>🕐 {activity.time}</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* 客户列表区域 */}
<div className={styles.tableCard}>
<div className={styles.tableHeader}>
<div className={styles.tableTitle}>客户列表</div>
</div>
{/* 筛选栏 */}
<div className={styles.filterBar}>
<div className={styles.filterGroup}>
<input
type="text"
className={styles.searchInput}
placeholder="搜索关键词"
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
1 month ago
/>
3 weeks ago
<select
className={styles.filterSelect}
value={customerType}
onChange={(e) => setCustomerType(e.target.value)}
>
<option value="全部">客户类型: 全部</option>
<option value="客户">客户</option>
<option value="供应商">供应商</option>
</select>
<select
className={styles.filterSelect}
value={customerLevel}
onChange={(e) => setCustomerLevel(e.target.value)}
>
<option value="全部">客户等级: 全部</option>
<option value="高价值">高价值</option>
<option value="常规客户">常规客户</option>
</select>
<select
className={styles.filterSelect}
value={cooperationStatus}
onChange={(e) => setCooperationStatus(e.target.value)}
>
<option value="全部">合作状态: 全部</option>
<option value="合作中">合作中</option>
<option value="暂停合作">暂停合作</option>
</select>
<button className={styles.queryBtn} onClick={() => console.log('查询')}>
🔍 查询
</button>
<button className={styles.resetBtn} onClick={() => {
setSearchKeyword('');
setCustomerType('全部');
setCustomerLevel('全部');
setCooperationStatus('全部');
}}>
🔄 重置
</button>
<button className={styles.addBtn}>
新增客户
</button>
<button className={styles.exportBtn}>
📥 批量导出
</button>
</div>
</div>
{/* 表格 */}
<div className={styles.tableWrapper}>
<table className={styles.dataTable}>
<thead>
<tr>
<th>
<input
type="checkbox"
checked={selectedRows.length === tableData.length}
onChange={handleSelectAll}
/>
</th>
<th>客户名称</th>
<th>联系人</th>
<th>联系电话</th>
<th>分类</th>
<th>交易额度()</th>
<th>合作状态</th>
<th>满意度</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{tableData.map((row) => (
<tr key={row.id}>
<td>
<input
type="checkbox"
checked={selectedRows.includes(row.id)}
onChange={() => handleSelectRow(row.id)}
/>
</td>
<td>{row.customerName}</td>
<td>{row.contact}</td>
<td>{row.phone}</td>
<td>
<span className={`${styles.tag} ${row.classification === '高价值' ? styles.tagGold : styles.tagBlue}`}>
{row.classification}
</span>
</td>
<td>¥{row.monthlyAmount.toLocaleString()}</td>
<td>
<span className={`${styles.tag} ${row.cooperationStatus === '合作中' ? styles.tagGreen : styles.tagOrange}`}>
{row.cooperationStatus}
</span>
</td>
<td>
<div className={styles.stars}>
{renderStars(row.satisfaction)}
</div>
</td>
<td>
<div className={styles.actionBtns}>
<button className={styles.actionBtn}>查看详情</button>
<button className={styles.actionBtn}>修改</button>
<button className={`${styles.actionBtn} ${styles.deleteBtn}`}>删除</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
1 month ago
</div>
3 weeks ago
{/* 分页 */}
<div className={styles.pagination}>
<div className={styles.paginationInfo}>
共85条
<select
className={styles.pageSizeSelect}
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
setCurrentPage(1);
}}
>
<option value="10">10 / page</option>
<option value="20">20 / page</option>
<option value="50">50 / page</option>
<option value="100">100 / page</option>
</select>
</div>
<div className={styles.paginationControls}>
<button
className={styles.pageBtn}
disabled={currentPage === 1}
onClick={() => setCurrentPage(currentPage - 1)}
>
</button>
{currentPage > 3 && (
<>
<button className={styles.pageBtn} onClick={() => setCurrentPage(1)}>1</button>
<span className={styles.pageEllipsis}>...</span>
</>
)}
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
let pageNum;
if (currentPage <= 3) {
pageNum = i + 1;
} else if (currentPage >= totalPages - 2) {
pageNum = totalPages - 4 + i;
} else {
pageNum = currentPage - 2 + i;
}
if (pageNum > totalPages) return null;
return (
<button
key={pageNum}
className={`${styles.pageBtn} ${currentPage === pageNum ? styles.pageBtnActive : ''}`}
onClick={() => setCurrentPage(pageNum)}
>
{pageNum}
</button>
);
})}
{currentPage < totalPages - 2 && (
<>
<span className={styles.pageEllipsis}>...</span>
<button className={styles.pageBtn} onClick={() => setCurrentPage(totalPages)}>{totalPages}</button>
</>
)}
<button
className={styles.pageBtn}
disabled={currentPage === totalPages}
onClick={() => setCurrentPage(currentPage + 1)}
>
</button>
</div>
</div>
</div>
1 month ago
</div>
);
};
export default CustomerInfoManagement;