CustomerList.jsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import { Card, Table, Button, Space, Tag, Input, Select, message, Modal } from 'antd'
  2. import { PlusOutlined, EyeOutlined, EditOutlined, SearchOutlined } from '@ant-design/icons'
  3. import { useEffect, useState } from 'react'
  4. import { useNavigate } from 'react-router-dom'
  5. import request from '../../utils/request'
  6. import { CUSTOMER_STATUS } from '../../utils/constants'
  7. import dayjs from 'dayjs'
  8. const { Search } = Input
  9. const { Option } = Select
  10. const CustomerList = () => {
  11. const [data, setData] = useState([])
  12. const [loading, setLoading] = useState(false)
  13. const [pagination, setPagination] = useState({ current: 1, pageSize: 20, total: 0 })
  14. const [filters, setFilters] = useState({ status: '', keyword: '' })
  15. const navigate = useNavigate()
  16. useEffect(() => {
  17. loadData()
  18. }, [pagination.current, pagination.pageSize, filters])
  19. const loadData = async () => {
  20. setLoading(true)
  21. try {
  22. const res = await request.get('/customers', {
  23. params: {
  24. page: pagination.current,
  25. limit: pagination.pageSize,
  26. ...filters
  27. }
  28. })
  29. setData(res.data.customers)
  30. setPagination(prev => ({ ...prev, total: res.data.pagination.total }))
  31. } catch (error) {
  32. console.error('加载数据失败:', error)
  33. } finally {
  34. setLoading(false)
  35. }
  36. }
  37. const handleSearch = (value) => {
  38. setFilters(prev => ({ ...prev, keyword: value }))
  39. setPagination(prev => ({ ...prev, current: 1 }))
  40. }
  41. const handleStatusChange = (value) => {
  42. setFilters(prev => ({ ...prev, status: value }))
  43. setPagination(prev => ({ ...prev, current: 1 }))
  44. }
  45. const handleTableChange = (newPagination) => {
  46. setPagination(newPagination)
  47. }
  48. const columns = [
  49. {
  50. title: '客户名称',
  51. dataIndex: 'customer_name',
  52. key: 'customer_name',
  53. width: 200,
  54. fixed: 'left',
  55. },
  56. {
  57. title: '行业',
  58. dataIndex: 'industry',
  59. key: 'industry',
  60. width: 120,
  61. },
  62. {
  63. title: '地区',
  64. dataIndex: 'region',
  65. key: 'region',
  66. width: 100,
  67. },
  68. {
  69. title: '联系人',
  70. dataIndex: 'contact_person',
  71. key: 'contact_person',
  72. width: 100,
  73. },
  74. {
  75. title: '联系电话',
  76. dataIndex: 'contact_phone',
  77. key: 'contact_phone',
  78. width: 120,
  79. },
  80. {
  81. title: '状态',
  82. dataIndex: 'status',
  83. key: 'status',
  84. width: 100,
  85. render: (status) => (
  86. <Tag color={CUSTOMER_STATUS[status]?.color}>
  87. {CUSTOMER_STATUS[status]?.text}
  88. </Tag>
  89. ),
  90. },
  91. {
  92. title: '负责人',
  93. dataIndex: 'owner_name',
  94. key: 'owner_name',
  95. width: 100,
  96. },
  97. {
  98. title: '保护期到期',
  99. dataIndex: 'protected_end_date',
  100. key: 'protected_end_date',
  101. width: 160,
  102. render: (text, record) => {
  103. const isExpired = record.is_expired === 1
  104. return (
  105. <span style={{ color: isExpired ? '#ff4d4f' : undefined }}>
  106. {dayjs(text).format('YYYY-MM-DD HH:mm')}
  107. </span>
  108. )
  109. },
  110. },
  111. {
  112. title: '报备时间',
  113. dataIndex: 'report_time',
  114. key: 'report_time',
  115. width: 160,
  116. render: (text) => dayjs(text).format('YYYY-MM-DD HH:mm'),
  117. },
  118. {
  119. title: '操作',
  120. key: 'action',
  121. fixed: 'right',
  122. width: 100,
  123. render: (_, record) => (
  124. <Space>
  125. <Button
  126. type="link"
  127. size="small"
  128. icon={<EyeOutlined />}
  129. onClick={() => navigate(`/customers/${record.id}`)}
  130. >
  131. 查看
  132. </Button>
  133. </Space>
  134. ),
  135. },
  136. ]
  137. return (
  138. <div>
  139. <Card>
  140. <Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
  141. <Space>
  142. <Search
  143. placeholder="搜索客户名称、联系人、电话"
  144. allowClear
  145. onSearch={handleSearch}
  146. style={{ width: 300 }}
  147. />
  148. <Select
  149. placeholder="客户状态"
  150. allowClear
  151. style={{ width: 120 }}
  152. onChange={handleStatusChange}
  153. >
  154. <Option value="following">跟进中</Option>
  155. <Option value="won">已成交</Option>
  156. <Option value="lost">已丢单</Option>
  157. <Option value="released">已释放</Option>
  158. </Select>
  159. </Space>
  160. <Button
  161. type="primary"
  162. icon={<PlusOutlined />}
  163. onClick={() => navigate('/customers/create')}
  164. >
  165. 报备客户
  166. </Button>
  167. </Space>
  168. <Table
  169. columns={columns}
  170. dataSource={data}
  171. rowKey="id"
  172. loading={loading}
  173. pagination={pagination}
  174. onChange={handleTableChange}
  175. scroll={{ x: 1400 }}
  176. />
  177. </Card>
  178. </div>
  179. )
  180. }
  181. export default CustomerList